Hey Guys, I like to know the correct way to calculate/use a height map in a surface shader? I'm making a Standard (metallic) shader with a heightmap texture. Here is what I currently have not sure if this is the right way to go about it. Code (CSharp): Shader "Custom/PBR/Standard Height" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _BumpMap("Normal Map", 2D) = "bump" {} _BumpScale ("Normal ", Float) = 1 _HeightMap ("Height Map", 2D) = "white" {} _HeightMapScale ("Height", Float) = 1 _Glossiness ("Smoothness", Range(0,1)) = 0.5 _MetallicGlossMap ("Metallic", 2D) = "white" {} _Metallic ("Metallic", Range(0,1)) = 0.0 _OcclusionMap("OcclusionMap", 2D) = "white" {} _OcclusionStrength("Occlusion Strength", Float) = 1 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows vertex:vert // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 #pragma glsl sampler2D _MainTex; sampler2D _OcclusionMap; sampler2D _BumpMap; sampler2D _MetallicGlossMap; sampler2D _HeightMap; struct Input { float2 uv_MainTex; }; half _HeightMapScale; half _Glossiness; half _Metallic; half _BumpScale; half _OcclusionStrength; fixed4 _Color; void vert(inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o); float4 heightMap = tex2Dlod(_HeightMap, float4(v.texcoord.xy,0,0)); //fixed4 heightMap = _HeightMap; v.vertex.z += heightMap.b * _HeightMapScale; } void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables fixed4 gloss = tex2D(_MetallicGlossMap, IN.uv_MainTex); o.Metallic = gloss.r * _Metallic; o.Smoothness = gloss.a * _Glossiness; o.Normal = UnpackScaleNormal(tex2D(_BumpMap, IN.uv_MainTex), _BumpScale); o.Occlusion = tex2D(_OcclusionMap, IN.uv_MainTex) * _OcclusionStrength; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
What effect are you expecting? There are many ways to use a heightmap with a surface shader, and the method you're using is a perfectly valid option, but I get the impression it isn't producing the result you want. A height map is just description of data in a texture, it doesn't really say anything about how you use it. If you're trying to replicate what the standard shader does with the heightmap, that's parallax offset mapping, which uses a heightmap to distort UVs to give a very basic appeance of additional surface depth. If you're looking to implement that in your surface shader see this post which includes an example of using ParallaxOffset, as well as a link to the more expensive, but better looking Parallax Occlusions Mapping. https://forum.unity3d.com/threads/surface-shader-parallaxoffset-issue.438089/
This is not at all the correct way to do it for a couple of reasons: 1.) You're doing it in the vertex program -- Height maps are per pixel, not per vertex. 2.) You're just blindly increasing the Z axis -- Height maps operate on the normal of the current surface.
Posting here since this page comes up as the topmost result whenever I google the issue, and because I finally figured it out. And to clarify, this is assuming you mean "Height Map" as in what the Standard Shader calls a Height Map, example below -- you can see the bricks stick out more as I drag the knob to the right... Interestingly enough, even though Unity calls it a "Height Map" in the Standard Shader, the Shader language refers to it as a parallax map (there's a hint in the Height Map documentation here: https://docs.unity3d.com/Manual/StandardShaderMaterialParameterHeightMap.html ) -- Discovering that makes it a bit easier to track down the proper code to make this work. (So yeah, turns out @bgolus was on the right track -- but saying "what you're doing looks valid" was misleading for me... anyway, if you're as thick as I can be, the step-by-step is below, give it a shot.) First off Declare two Properties for your shader: Code (csharp): _HeightMap("Height Map", 2D) = "white" {} _HeightPower("Height Power", Range(0,.125)) = 0 Add two globals to match: Code (csharp): sampler2D _HeightMap; float _HeightPower; to your "Input" struct, add two fields: Code (csharp): float2 uv_HeightMap; float3 viewDir; if your "surf" program, for the very first line add: Code (csharp): float2 texOffset = ParallaxOffset(tex2D(_HeightMap, IN.uv_HeightMap).r, _HeightPower, IN.viewDir); then find all your other tex2D lookups and adjust them, like so: Code (csharp): fixed4 c = tex2D(_MainTex, IN.uv_MainTex + texOffset) * _Color; o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap + texOffset)); //... o.Albedo = c.rgb; o.Alpha = c.a; Crazily enough, all it's doing is modifying the UVs based on the current view direction to simulate a height effect... it works really well though.