Search Unity

Parallax Mapping in Unity

Discussion in 'Made With Unity' started by PeterDahl, Aug 18, 2005.

  1. PeterDahl

    PeterDahl

    Joined:
    Jun 13, 2005
    Posts:
    61
    We just did a Virtual Displacement Mapping (Parallax Bumpmapping) shader, that basically is a improved bumpmap shader. It will be in Unity 1.1 when it comes out. Here is a screenshot of the new parallax shader along with a shot rendered with the old bump shader for comparison:

    The picture on top is with the Parallax method, while the bottom picture were rendered with normal bumpmapping.

    The Height variable (in the material inspector on the right), can be used to control the amount of displacement. Lesser height will give a more souble effect, that also will be less prone to artifacts in shallow angles.
    Please let me know if you have sugestions for this shader that could improve it further :)

    - Peter, OTEE shader programmer
     

    Attached Files:

  2. robertseadog

    robertseadog

    Joined:
    Jul 23, 2005
    Posts:
    374
    Looks very promising! Could This be used on animated characters as well?
     
  3. DaveyJJ

    DaveyJJ

    Joined:
    Mar 24, 2005
    Posts:
    1,558
    Darn, you folks are good.
     
  4. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Just curious, would it be possible to have access to that texture you used? I was playing around with some other bump maps I have, and couldn't get the same effect...
     
  5. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    It is included in /Users/Shared/Unity/Examples/Assets/Parrallax
     
  6. klindeman

    klindeman

    Joined:
    Jun 13, 2005
    Posts:
    295
    Oh hey, didn't even think to look.

    You guys rock!
     
  7. guategeek_legacy

    guategeek_legacy

    Joined:
    Jun 22, 2005
    Posts:
    659
    Thanks guys. I was going to ask for a slider that would adjust the bump maps efect on the texter. I guess before you would of had to change the bump map itself? Jeff
     
  8. verbatimline

    verbatimline

    Joined:
    Mar 24, 2009
    Posts:
    88
    That is phenomenal guys. Is there a link to the source code?
     
  9. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    thats a stoneage thread. that shader is part of unity 2.x, its sources part of the shader package you can download from the resource section :)
     
  10. twitchfactor

    twitchfactor

    Joined:
    Mar 8, 2009
    Posts:
    356
    Why would you want to, unless you were doing a Shadow of Collossass clone?

    Honestly, I even see such silliness in professional game making, but the reality is; parallax mapping is wasteful on anything but large walls.

    Cool shader, though...
     
  11. verbatimline

    verbatimline

    Joined:
    Mar 24, 2009
    Posts:
    88
    hey twitchfactor,
    how is it wastefull on anything but large walls?
     
  12. minevr

    minevr

    Joined:
    Mar 4, 2008
    Posts:
    1,018


    Shader:

    Code (csharp):
    1. Shader "ReliefShader" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.         _ReliefMap ("Relief Map (RGBA)", 2D) = "white" {}
    5.         _Depth ("Depth", Range (0.01, 1)) = 0.1
    6.         _AmbientColor ("Ambient Color", Color) = (0.184314, 0.192157, 0.27451, 1.0)
    7.         _DiffuseColor ("Diffuse Color", Color) = (0.552941, 0.588235, 0.611765, 1.0)
    8.         _SpecularColor ("Specular Color", Color) = (0.65098, 0.556863, 0.439216, 1.0)
    9.         _LightPosition ("Light Position", Vector) = (100,100,100)
    10.         _LightColor ("Light Color", Color) = (1,1,1,1)
    11.     }
    12.     SubShader {
    13.    
    14.         // Pass 1
    15.    
    16.         Pass {
    17.  
    18. CGPROGRAM
    19.  
    20. #pragma target 3.0
    21. #pragma vertex av //ambient vertex
    22. #pragma fragment af //ambient fragment
    23. // new option below
    24. #pragma profileoption MaxTexIndirections=1024
    25. #include "UnityCG.cginc"
    26.  
    27. uniform sampler2D _MainTex;
    28. uniform sampler2D _ReliefMap;
    29. uniform float _Depth;
    30. uniform float4 _AmbientColor;
    31. uniform float3 _LightPosition;
    32.  
    33. // setup ray pos and dir based on view vector
    34. // and apply depth bias and depth factor
    35. void setup_ray(float2 texcoord, float3 eyeVec,out float3 p,out float3 v, float depth)
    36. {
    37.     p = float3(texcoord,0);
    38.  
    39.     v = normalize(eyeVec);
    40.     v.x = -v.x;             // Negate X axis (Max needs -XZ due to Z axis being up)
    41.     v.z = -v.z;             // Negate Z axis (Max needs -XZ due to Z axis being up)
    42.     v.z = abs(v.z);
    43.  
    44.         float db = 1.0-v.z; db*=db; db=1.0-db*db;
    45.         v.xy *= db;
    46.  
    47.     v.xy *= _Depth;
    48. }
    49.  
    50. // ray intersect depth map using linear and binary searches
    51. // depth value stored in alpha channel
    52. void ray_intersect_relief(inout float3 p,inout float3 v)
    53. {
    54.     const int num_steps_lin=24;
    55.     const int num_steps_bin=6;
    56.    
    57.     v /= v.z*num_steps_lin;
    58.    
    59.     int i;
    60.     for( i=0;i<num_steps_lin;i++ )
    61.     {
    62.         //float4 tex = tex2D(ReliefMapping_5784Sampler, p.xy);
    63.         float4 tex = tex2D(_ReliefMap, p.xy);
    64.         tex.w = 1 - tex.w;
    65.         if (p.z<tex.w)
    66.             p+=v;
    67.     }
    68.    
    69.     for( i=0;i<num_steps_bin;i++ )
    70.     {
    71.         v *= 0.5;
    72.         //float4 tex = tex2D(ReliefMapping_5784Sampler, p.xy);
    73.         float4 tex = tex2D(_ReliefMap, p.xy);
    74.         tex.w = 1 - tex.w;
    75.         if (p.z<tex.w)
    76.             p+=v;
    77.         else
    78.             p-=v;
    79.     }
    80. }
    81.  
    82. // vertex input: position, uv1, uv2
    83. struct a2v {
    84.     float4 vertex : POSITION;
    85.     float4 tangent      : TANGENT;
    86.     float3 normal       : NORMAL;
    87.     float2 texCoord     : TEXCOORD0;
    88. };
    89.  
    90. struct v2f {
    91.         float4 vertex : POSITION;
    92.         //float4 uv : TEXCOORD0;
    93.         //float4 uv1 : TEXCOORD1;
    94.         //float4 uv2 : TEXCOORD2;
    95.         float3 lightVec         : TEXCOORD0;
    96.         float3 eyeVec           : TEXCOORD1;
    97.         float2 texCoord         : TEXCOORD2;
    98. };
    99.  
    100. //Ambient and Self-Illum Pass Vertex Shader
    101. //v2f av(a2v In, uniform float3 lightPosition)
    102. v2f av(a2v In)
    103. {
    104.     v2f Out = (v2f)0;
    105.    
    106.     // calculate binormal - no semantic for them
    107.     float3 binormal = cross( In.normal, In.tangent.xyz ) * In.tangent.w;
    108.    
    109.     //Out.position = mul(In.vertex, wvp);               //transform vert position to homogeneous clip space
    110.     //Out.vertex = mul(In.vertex, glstate.matrix.mvp);              //transform vert position to homogeneous clip space
    111.     Out.vertex = mul(glstate.matrix.mvp, In.vertex);                //transform vert position to homogeneous clip space
    112.    
    113.     //this code was added by the Relief Mapping Node
    114.     Out.texCoord = In.texCoord;                     //pass through texture coordinates from channel 1
    115.     //this code was added by the Eye Vector Node
    116.     float3x3 objTangentXf;                              //build object to tangent space transform matrix
    117.     //objTangentXf[0] = In.tangent;
    118.     objTangentXf[0] = In.tangent.xyz;
    119.     objTangentXf[1] = -binormal;
    120.     objTangentXf[2] = In.normal;
    121.     //these three lines were added by the Eye Vector Node
    122.     //float4 osIPos = mul(viewInv[3], worldI);          //put world space eye position in object space
    123.     //float3 osIVec = osIPos.xyz - In.vertex.xyz;       //object space eye vector
    124.     float3 osIVec = _ObjectSpaceCameraPos - In.vertex.xyz;      //object space eye vector
    125.     //float3 osIVec = In.vertex.xyz - _ObjectSpaceCameraPos;        //object space eye vector
    126.     Out.eyeVec = mul(objTangentXf, osIVec);             //tangent space eye vector passed out
    127.    
    128.     return Out;
    129. }
    130.  
    131.  
    132. //Ambient and Self-Illum Pass Pixel Shader
    133. //float4 af(v2f In, uniform float4 lightColor) : COLOR
    134. float4 af(v2f In) : COLOR
    135. {
    136.     float3 V = normalize(In.eyeVec.xyz);        //normalized eye vector
    137.     float2 ReliefMapping_5784UVs = In.texCoord.xy;
    138.     float3 ReliefMapping_5784p,ReliefMapping_5784v;
    139.     //setup_ray(ReliefMapping_5784UVs, V, ReliefMapping_5784p, ReliefMapping_5784v, depth);
    140.     setup_ray(ReliefMapping_5784UVs, V, ReliefMapping_5784p, ReliefMapping_5784v, _Depth);
    141.     ray_intersect_relief(ReliefMapping_5784p.xyz,ReliefMapping_5784v);
    142.     float2 ReliefMapping_5784NewUVs = ReliefMapping_5784p.xy;
    143.     //float4 ReliefMapping_5784Normal = tex2D(ReliefMapping_5784Sampler,ReliefMapping_5784NewUVs);
    144.     float4 ReliefMapping_5784Normal = tex2D(_ReliefMap, ReliefMapping_5784NewUVs);
    145.     ReliefMapping_5784Normal.xyz = ReliefMapping_5784Normal.xyz * 2 - 1;
    146.     ReliefMapping_5784Normal.z = sqrt(1.0 - dot(ReliefMapping_5784Normal.xy,ReliefMapping_5784Normal.xy));
    147.     ReliefMapping_5784Normal.xyz = normalize(ReliefMapping_5784Normal.xyz);         //normalized the normal vector
    148.     //float4 TextureMap_7216 = tex2D(TextureMap_7216Sampler, ReliefMapping_5784NewUVs.xy);
    149.     float4 TextureMap_7216 = tex2D(_MainTex, ReliefMapping_5784NewUVs.xy);
    150.     //float3 MultiplyAmbient = UIColor_6122 * TextureMap_7216.rgb;
    151.     //float3 MultiplyAmbient = _AmbientColor * TextureMap_7216.rgb;
    152.     float3 MultiplyAmbient = _AmbientColor.rgb * TextureMap_7216.rgb;
    153.     float3 input1 = MultiplyAmbient;
    154.    
    155.     float4 ret =  float4(0,0,0,1);
    156.     ret = float4(input1, 1);
    157.     return ret;
    158. }
    159.  
    160.  
    161. ENDCG
    162.  
    163.         }
    164.        
    165.         // PASS 2
    166.        
    167.         Pass {
    168.  
    169. CGPROGRAM
    170.  
    171. #pragma target 3.0
    172. #pragma vertex v //diffuse and specular vertex
    173. #pragma fragment f //diffuse and specular fragment
    174. // new option below
    175. #pragma profileoption MaxTexIndirections=1024
    176. #include "UnityCG.cginc"
    177.  
    178. uniform sampler2D _MainTex;
    179. uniform sampler2D _ReliefMap;
    180. uniform float _Depth;
    181. uniform float4 _AmbientColor;
    182. uniform float4 _DiffuseColor;
    183. uniform float4 _SpecularColor;
    184. uniform float3 _LightPosition;
    185. uniform float4 _LightColor;
    186.  
    187. // setup ray pos and dir based on view vector
    188. // and apply depth bias and depth factor
    189. void setup_ray(float2 texcoord, float3 eyeVec,out float3 p,out float3 v, float depth)
    190. {
    191.     p = float3(texcoord,0);
    192.  
    193.     v = normalize(eyeVec);
    194.     v.x = -v.x;             // Negate X axis (Max needs -XZ due to Z axis being up)
    195.     v.z = -v.z;             // Negate Z axis (Max needs -XZ due to Z axis being up)
    196.     v.z = abs(v.z);
    197.  
    198.         float db = 1.0-v.z; db*=db; db=1.0-db*db;
    199.         v.xy *= db;
    200.  
    201.     v.xy *= _Depth;
    202. }
    203.  
    204. // ray intersect depth map using linear and binary searches
    205. // depth value stored in alpha channel
    206. void ray_intersect_relief(inout float3 p,inout float3 v)
    207. {
    208.     const int num_steps_lin=24;
    209.     const int num_steps_bin=6;
    210.    
    211.     v /= v.z*num_steps_lin;
    212.    
    213.     int i;
    214.     for( i=0;i<num_steps_lin;i++ )
    215.     {
    216.         //float4 tex = tex2D(ReliefMapping_5784Sampler, p.xy);
    217.         float4 tex = tex2D(_ReliefMap, p.xy);
    218.         tex.w = 1 - tex.w;
    219.         if (p.z<tex.w)
    220.             p+=v;
    221.     }
    222.    
    223.     for( i=0;i<num_steps_bin;i++ )
    224.     {
    225.         v *= 0.5;
    226.         //float4 tex = tex2D(ReliefMapping_5784Sampler, p.xy);
    227.         float4 tex = tex2D(_ReliefMap, p.xy);
    228.         tex.w = 1 - tex.w;
    229.         if (p.z<tex.w)
    230.             p+=v;
    231.         else
    232.             p-=v;
    233.     }
    234. }
    235.  
    236. // vertex input: position, uv1, uv2
    237. struct a2v {
    238.     float4 vertex : POSITION;
    239.     float4 tangent      : TANGENT;
    240.     float3 normal       : NORMAL;
    241.     float2 texCoord     : TEXCOORD0;
    242. };
    243.  
    244. struct v2f {
    245.         float4 vertex : POSITION;
    246.         //float4 uv : TEXCOORD0;
    247.         //float4 uv1 : TEXCOORD1;
    248.         //float4 uv2 : TEXCOORD2;
    249.         float3 lightVec         : TEXCOORD0;
    250.         float3 eyeVec           : TEXCOORD1;
    251.         float2 texCoord         : TEXCOORD2;
    252. };
    253.  
    254. //Diffuse and Specular Pass Vertex Shader
    255. //v2f v(a2v In, uniform float3 lightPosition)
    256. v2f v(a2v In)
    257. {
    258.     v2f Out = (v2f)0;
    259.     //Out.position = mul(In.position, wvp);             //transform vert position to homogeneous clip space
    260.     //Out.vertex = mul(In.vertex, glstate.matrix.mvp);              //transform vert position to homogeneous clip space
    261.     Out.vertex = mul(glstate.matrix.mvp, In.vertex);                //transform vert position to homogeneous clip space
    262.    
    263.     // calculate binormal - no semantic for them
    264.     float3 binormal = cross( In.normal, In.tangent.xyz ) * In.tangent.w;
    265.    
    266.     //this code was added by the standard material
    267.     float3x3 objTangentXf;                              //build object to tangent space transform matrix
    268.         objTangentXf[0] = In.tangent;
    269.         objTangentXf[1] = -binormal;
    270.         objTangentXf[2] = In.normal;
    271.     //this code was added by the standard material
    272.     //float3 wsLPos = mul(In.vertex, world).xyz;            //put the vert position in world space
    273.     float3 wsLPos = mul(In.vertex.xyz, (float3x3)_Object2World);            //put the vert position in world space
    274.     float3 wsLVec = _LightPosition - wsLPos;    //cast a ray to the light
    275.     //float3 osLVec = mul(wsLVec, worldI).xyz;  //transform the light vector to object space
    276.     float3 osLVec = mul(wsLVec, (float3x3)_World2Object).xyz;  //transform the light vector to object space
    277.     Out.lightVec = mul(objTangentXf, osLVec);           //tangent space light vector passed out
    278.     //this code was added by the standard material
    279.     //float4 osIPos = mul(viewInv[3], worldI);          //put world space eye position in object space
    280.     //float3 osIVec = osIPos.xyz - In.vertex.xyz;       //object space eye vector
    281.     float3 osIVec = _ObjectSpaceCameraPos - In.vertex.xyz;      //object space eye vector
    282.     Out.eyeVec = mul(objTangentXf, osIVec);             //tangent space eye vector passed out
    283.  
    284.     //this code was added by the Relief Mapping Node
    285.     Out.texCoord = In.texCoord;                     //pass through texture coordinates from channel 1
    286.  
    287.     return Out;
    288. }
    289.  
    290. //Diffuse and Specular Pass Pixel Shader
    291. //float4 f(v2f In, uniform float4 lightColor) : COLOR
    292. float4 f(v2f In) : COLOR
    293. {
    294.     float3 ret = float3(0,0,0);
    295.     float3 V = normalize(In.eyeVec);        //creating the eye vector  
    296.     float3 L = normalize(In.lightVec);      //creating the light vector  
    297.  
    298.     float2 ReliefMapping_5784UVs = In.texCoord.xy;
    299.     float3 ReliefMapping_5784p,ReliefMapping_5784v;
    300.     setup_ray(ReliefMapping_5784UVs, V, ReliefMapping_5784p, ReliefMapping_5784v, _Depth);
    301.     ray_intersect_relief(ReliefMapping_5784p.xyz,ReliefMapping_5784v);
    302.     float2 ReliefMapping_5784NewUVs = ReliefMapping_5784p.xy;
    303.     //float4 ReliefMapping_5784Normal = tex2D(ReliefMapping_5784Sampler,ReliefMapping_5784NewUVs);
    304.     float4 ReliefMapping_5784Normal = tex2D(_ReliefMap,ReliefMapping_5784NewUVs);
    305.     ReliefMapping_5784Normal.xyz = ReliefMapping_5784Normal.xyz * 2 - 1;
    306.     ReliefMapping_5784Normal.z = sqrt(1.0 - dot(ReliefMapping_5784Normal.xy,ReliefMapping_5784Normal.xy));
    307.     ReliefMapping_5784Normal.xyz = normalize(ReliefMapping_5784Normal.xyz);         //normalized the normal vector
    308.     //float4 TextureMap_7216 = tex2D(TextureMap_7216Sampler, ReliefMapping_5784NewUVs.xy);
    309.     float4 TextureMap_7216 = tex2D(_MainTex, ReliefMapping_5784NewUVs.xy);
    310.     //float3 MultiplyDiffuse = TextureMap_7216.rgb * UIColor_984;
    311.     float3 MultiplyDiffuse = TextureMap_7216.rgb * _DiffuseColor.rgb;
    312.     float3 input2 = MultiplyDiffuse;
    313.  
    314.  
    315.     //float3 input3 = UIColor_8972;
    316.     float3 input3 = _SpecularColor.rgb;
    317.  
    318.  
    319.     float3 input8 = ReliefMapping_5784Normal.xyz;
    320.  
    321.     float3 N = input8;                      //using the Normal socket  
    322.     float3 diffuseColor = input2;           //using the Diffuse Color socket  
    323.     float diffuse = saturate(dot(N,L));     //calculate the diffuse  
    324.     diffuseColor *= diffuse;                //the resulting diffuse color  
    325.     ret += diffuseColor;                    //add diffuse light to final color  
    326.     float3 specularColor = input3;          //using the Specular Color socket
    327.     float glossiness = 20;                  //the Glossiness socket was empty - using default value
    328.     float3 H = normalize(L + V);            //Compute the half angle  
    329.     float NdotH = saturate(dot(N,H));       //Compute NdotH  
    330.     specularColor *= pow(NdotH, glossiness);//Raise to glossiness power and compute final specular color  
    331.     ret += specularColor;                   //add specular light to final color  
    332.     //ret *= lightColor;                        //multiply by the color of the light
    333.     ret *= _LightColor.rgb;                     //multiply by the color of the light
    334.     float4 done = float4(ret, 1);
    335.     return done;
    336. }
    337.  
    338.  
    339. ENDCG
    340.  
    341.         }
    342.     }
    343.     Fallback "Vertex Colored", 1
    344. }
     
  13. twitchfactor

    twitchfactor

    Joined:
    Mar 8, 2009
    Posts:
    356
    The difference between a good normal and a parallax map on anything other than a long contiguous piece of geo is negligible. But the shader is more complex, often making things slower than they need to be.

    I don't want to be anyone to discourage use of such a fine shader/technique, just people should be mindful of proper use. In my experience, walls and floors are excellent candidates for parallax mapping. Characters are not.