Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. How can we improve our URP documentation to cover your needs? Give us your feedback. Take our survey and let us know.
    Dismiss Notice

Unity 3 + Support for specular textures

Discussion in 'Shaders' started by Tim-C, Jul 31, 2010.

  1. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,165
    Hi,

    I have been doing a lot of shader work in unity3 beta, and am really enjoying it but some of the artists I have been working with are getting a bit annoyed. The reason is that in unity 3 (even with custom shaders) there is no way to output a specular color to the surface descriptor. The output structure only takes a specular power and a specular brightness (specular / gloss respectively).

    I believe the reason for this is that the output structure is basically 3 half4's and these go into the G-Buffer for deferred rendering.

    Code (csharp):
    1.  
    2. struct SurfaceOutput {
    3.     half3 Albedo;
    4.     half3 Normal;
    5.     half3 Emission;
    6.     half Specular;
    7.     half Gloss;
    8.     half Alpha;
    9. };
    10.  
    Because of G-Buffer not being fat enough there does not seem to be an easy way to get per pixel specular color into the final render output. And looking at the shaders that come with Unity3 it seems that per material specular color is kind of a hack at the moment (hard coded as float4 _SpecColor).

    There are two ways that this could be fixed:
    1) Add an extra half4 channel added to the surface output structure. This could be a custom channel, that can be used in the lighting shader at the discretion of the shader programmer.
    2) Allow the emission channel to be used in this way for custom lighting, and provide a flag on the shader that tells unity internals to not perform the emissive pass when rending the material, then in the lighting passes the emissive value can be used by the custom lighting shader.

    I can understand why you wouldn't want to make the g-buffer fatter, but having a bit more customization on what the output channels are used for would be very nice :D
     
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,356
    Unity 3 uses deferred lighting, not deferred shading. Specular colour is not stored in a G buffer. What happens is:

    • 1. Pre-pass G buffer(s) are filled with per-pixel depth, normal, layer and specular power. I believe MRTs are not used, so it's just the standard depth buffer and something like Normal (RG) Power (B) Layer (A).

      2. Irradiance is computed and stored in a lighting buffer, Diffuse (RGB) Specular (A). Note that the one channel for specular is a limitation of light colour, not surface colour: lights can't have different diffuse and specular colours, which they couldn't have in Unity 2 either.

      3. Shading pass takes incoming light values and uses them with the remaining surface properties. Among other things, diffuse surface colour is applied. You could also apply a specular surface colour here.

    The following shader works with both forward and deferred rendering in Unity 3:

    Code (csharp):
    1. Shader "Specular Colored" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.         _GlossTex ("Specular (RGB) Shininess (A)", 2D) = "gray" {}
    6.     }
    7.     SubShader {
    8.         Tags { "RenderType" = "Opaque" }
    9.         CGPROGRAM
    10.             //use custom lighting functions
    11.             #pragma surface surf BlinnPhongColor
    12.            
    13.             //custom surface output structure
    14.             struct SurfaceOutputSpecColor {
    15.                 half3 Albedo;
    16.                 half3 Normal;
    17.                 half3 Emission;
    18.                 half Specular;
    19.                 half3 GlossColor; //Gloss is now three-channel
    20.                 half Alpha;
    21.             };
    22.            
    23.             //forward lighting function
    24.             inline half4 LightingBlinnPhongColor (SurfaceOutputSpecColor s, half3 lightDir, half3 viewDir, half atten) {
    25.                 #ifndef USING_DIRECTIONAL_LIGHT
    26.                 lightDir = normalize(lightDir);
    27.                 #endif
    28.                 viewDir = normalize(viewDir);
    29.                 half3 h = normalize (lightDir + viewDir);
    30.                
    31.                 half diff = max (0, dot (s.Normal, lightDir));
    32.                
    33.                 float nh = max (0, dot (s.Normal, h));
    34.                 float spec = pow (nh, s.Specular*128.0);
    35.                
    36.                 half4 c;
    37.                 //Use gloss colour instead of gloss
    38.                 c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * s.GlossColor * spec) * (atten * 2);
    39.                 //We use gloss luminance to determine its overbright contribution
    40.                 c.a = s.Alpha + _LightColor0.a * Luminance(s.GlossColor) * spec * atten;
    41.                 return c;
    42.             }
    43.            
    44.             //deferred lighting function
    45.             inline half4 LightingBlinnPhongColor_PrePass (SurfaceOutputSpecColor s, half4 light) {
    46.                 //Use gloss colour instead of gloss
    47.                 half3 spec = light.a * s.GlossColor;
    48.                
    49.                 half4 c;
    50.                 c.rgb = (s.Albedo * light.rgb + light.rgb * spec.rgb);
    51.                 //We use gloss luminance to determine its overbright contribution
    52.                 c.a = s.Alpha + Luminance(spec);
    53.                 return c;
    54.             }
    55.            
    56.             sampler2D _MainTex;
    57.             sampler2D _GlossTex;
    58.             float4 _Color;
    59.            
    60.             struct Input {
    61.                 float2 uv_MainTex;
    62.                 float2 uv_GlossTex;
    63.             };
    64.            
    65.             void surf (Input IN, inout SurfaceOutputSpecColor o) {
    66.                 half4 tex = tex2D(_MainTex, IN.uv_MainTex);
    67.                 half4 gloss = tex2D(_GlossTex, IN.uv_GlossTex);
    68.                 o.Albedo = tex.rgb * _Color.rgb;
    69.                 //Gloss colour come from RGB
    70.                 o.GlossColor = gloss.rgb;
    71.                 o.Alpha = tex.a * _Color.a;
    72.                 //Specular is mapped
    73.                 o.Specular = gloss.a;
    74.             }
    75.         ENDCG
    76.     }
    77.     Fallback "Diffuse"
    78. }
     
  3. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,165
    Deferred rendering and shading are the same thing :p
    http://en.wikipedia.org/wiki/Deferred_shading

    This is different to how i have seen g-buffers done before, normally they describe all the surface properties. ie. surface color, surface specular color, spec power, normal emission ect.

    I am nor referring to the specular color of the lights, but the specular color of the surface. It is possible for a surface's specular reflection to be different from it's diffuse color.

    Looking at your example it is possible to add additional outputs to the surface output structure. After looking through the documentation I could not find any references to this. I had assumed that this was a hardcoded g-buffer description.

    Actually, I just did some testing, it seems that the maximum size of the output structure is 6 half4's. To me this implies that this structure is packed as the G-Buffer, as it needs to be used by the lighting passes.

    I believe this is happening:

    Pass 1: Perform the surface function. Pack all the values into the output structure as defined by the user. This is put into a G-Buffer that is packed / configured by unity based on how you define with a maximum size of 6 half4's.

    Pass 2: For each light sample the G-Buffer and render into the framebuffer using the defined lighting algorithm. The surface structure will have been sampled back from the g-buffer into a usable surface structure.

    My issue was that I did not know it was possible to define a custom output structure! It still looks like there is some special case handling in there for emissive though. It would be nice if this could be exposed in a Lighting_NoLight pass so you could define how you wanted to do the ambient / base pass.
     
  4. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,165
    Disregard my last post, you are talking about a Light Prepass renderer. And now it all makes sense.

    I'm silly and wrong. Thank you for taking the time to help me out.
     
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,356
    I'm glad it all makes sense. Deferred lighting vs. shading is a subtle name difference for two fairly different types of deferred rendering. One advantage deferred lighting has is that lets you have more complex material definitions, as long as they don't change the lighting calculations. If Unity 3 used deferred shading and didn't support specular colour, you'd be out of luck.
     
  6. RElam

    RElam

    Joined:
    Nov 16, 2009
    Posts:
    375
    Not if the materials and their output were properly abstracted. Then the engine could plug in the proper inputs and outputs to the shaders for surface creation and lighting. For instance, most would prioritize emission lower than specular, and push emission to a unique pass when needed (since very few surfaces emit light, in normal content), but if doing a tron like game, they might be happier dropping unique specular in favor of faster emission color. With a proper framework, you'd be hard pressed to come up with notable limitations of a deferred shader.
     
  7. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,356
    That would be cool, but I asked Aras and it's not happening for Unity 3. I don't know enough about graphics engine creation to tell if it's a ridiculous request or not. Completely custom lighting models (not just deciding how to interpret incoming Blinn-Phong irradiance values) would be a lot of fun.
     
  8. RElam

    RElam

    Joined:
    Nov 16, 2009
    Posts:
    375
    Oh, it's not ridiculous at all, unless you're hand coding freaking everything :evil:. I'm actually pretty disappointed looking at these shaders, I was all excited about Unity 3 having a deferred shader, now I realize it'll be pointless to try and move my terrain shader I was planning on from my engine, as it'd be too expensive in Unity. And it's looking like to create unique lighting we're still stuck replacing every shader in use. This honestly just seems like an excuse to have the deferred keyword in their list of features. Hopes dashed, sad and disappointed, I am. Sorry for thread hijack though, I'll go cry alone now :(.
     
  9. michaelvoigt

    michaelvoigt

    Joined:
    Oct 16, 2007
    Posts:
    17
    Works great, I have extended it with a normal map and shininess setting:

    Code (csharp):
    1. Shader "Specular Colored" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.         _GlossTex ("Specular (RGB) Shininess (A)", 2D) = "gray" {}
    6.         _BumpMap ("Normalmap", 2D) = "bump" {}
    7.         _Shininess ("Shininess", Range (0, 10)) = 1
    8.     }
    9.     SubShader {
    10.         Tags { "RenderType" = "Opaque" }
    11.         CGPROGRAM
    12.             //use custom lighting functions
    13.             #pragma surface surf BlinnPhongColor
    14.            
    15.            
    16.             //custom surface output structure
    17.             struct SurfaceOutputSpecColor {
    18.                 half3 Albedo;
    19.                 half3 Normal;
    20.                 half3 Emission;
    21.                 half Specular;
    22.                 half3 GlossColor; //Gloss is now three-channel
    23.                 half Alpha;
    24.                
    25.                
    26.             };
    27.            
    28.             //forward lighting function
    29.             inline half4 LightingBlinnPhongColor (SurfaceOutputSpecColor s, half3 lightDir, half3 viewDir, half atten) {
    30.                 #ifndef USING_DIRECTIONAL_LIGHT
    31.                 lightDir = normalize(lightDir);
    32.                 #endif
    33.                 viewDir = normalize(viewDir);
    34.                 half3 h = normalize (lightDir + viewDir);
    35.                
    36.                 half diff = max (0, dot (s.Normal, lightDir));
    37.                
    38.                 float nh = max (0, dot (s.Normal, h));
    39.                 float spec = pow (nh, s.Specular*128.0);
    40.                
    41.                 half4 c;
    42.                 //Use gloss colour instead of gloss
    43.                 c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * s.GlossColor * spec) * (atten * 2);
    44.                 //We use gloss luminance to determine its overbright contribution
    45.                 c.a = s.Alpha + _LightColor0.a * Luminance(s.GlossColor) * spec * atten;
    46.                 return c;
    47.             }
    48.            
    49.             //deferred lighting function
    50.             inline half4 LightingBlinnPhongColor_PrePass (SurfaceOutputSpecColor s, half4 light) {
    51.                 //Use gloss colour instead of gloss
    52.                 half3 spec = light.a * s.GlossColor;
    53.                
    54.                 half4 c;
    55.                 c.rgb = (s.Albedo * light.rgb + light.rgb * spec.rgb);
    56.                 //We use gloss luminance to determine its overbright contribution
    57.                 c.a = s.Alpha + Luminance(spec);
    58.                 return c;
    59.             }
    60.            
    61.             sampler2D _MainTex;
    62.             sampler2D _GlossTex;
    63.             sampler2D _BumpMap;
    64.             float4 _Color;
    65.             float _Shininess;
    66.            
    67.             struct Input {
    68.                 float2 uv_MainTex;
    69.                 float2 uv_GlossTex;
    70.                 float2 uv_BumpMap;
    71.  
    72.             };
    73.            
    74.             void surf (Input IN, inout SurfaceOutputSpecColor o) {
    75.                 half4 tex = tex2D(_MainTex, IN.uv_MainTex);
    76.                 half4 gloss = tex2D(_GlossTex, IN.uv_GlossTex);
    77.                 o.Albedo = tex.rgb * _Color.rgb;
    78.                 //Gloss colour come from RGB
    79.                 o.GlossColor = _Shininess * gloss.rgb;
    80.                 o.Alpha = tex.a * _Color.a;
    81.                 //Specular is mapped
    82.                 o.Specular = gloss.a;
    83.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
    84.             }
    85.         ENDCG
    86.     }
    87.     Fallback "Diffuse"
    88. }
     
  10. michaelvoigt

    michaelvoigt

    Joined:
    Oct 16, 2007
    Posts:
    17
    I have produced a streamlined much faster version that seems to look just as good as the original:

    Code (csharp):
    1. Shader "Specular Colored fast" {
    2.     Properties {
    3.         _Color ("Main Color", Color) = (1,1,1,1)
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.         _GlossTex ("Specular (RGB) Shininess (A)", 2D) = "gray" {}
    6.         _BumpMap ("Normalmap", 2D) = "bump" {}
    7.         _Shininess ("Shininess", Range (0, 10)) = 1
    8.     }
    9.     SubShader {
    10.         Tags { "RenderType" = "Opaque" }
    11.         CGPROGRAM
    12.             //use custom lighting functions
    13.             #pragma surface surf BlinnPhongColor
    14.            
    15.            
    16.             //custom surface output structure
    17.             struct SurfaceOutputSpecColor {
    18.                 half3 Albedo;
    19.                 half3 Normal;
    20.                 half3 Emission;
    21.                 half Specular;
    22.                 half3 GlossColor; //Gloss is now three-channel
    23.                 half Alpha;
    24.                
    25.                
    26.             };
    27.            
    28.             //forward lighting function
    29.             inline half4 LightingBlinnPhongColor (SurfaceOutputSpecColor s, half3 lightDir, half3 viewDir, half atten) {
    30.                
    31.                 half3 h = normalize (lightDir + viewDir);
    32.                
    33.                 half diff = max (0, dot (s.Normal, lightDir));
    34.                
    35.                 float nh = max (0, dot (s.Normal, h));
    36.                 float spec = pow (nh, s.Specular*128.0);
    37.                
    38.                 half4 c;
    39.                 //Use gloss colour instead of gloss
    40.                 c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * s.GlossColor * spec) * (atten * 2);
    41.                 //We use gloss luminance to determine its overbright contribution
    42.                 return c;
    43.             }
    44.            
    45.        
    46.             sampler2D _MainTex;
    47.             sampler2D _GlossTex;
    48.             sampler2D _BumpMap;
    49.             float _Shininess;
    50.            
    51.             struct Input {
    52.                 float2 uv_MainTex;
    53.             };
    54.            
    55.             void surf (Input IN, inout SurfaceOutputSpecColor o) {
    56.                 half4 tex = tex2D(_MainTex, IN.uv_MainTex);
    57.                 half4 gloss = tex2D(_GlossTex, IN.uv_MainTex);
    58.                 o.Albedo = tex.rgb;
    59.                 //Gloss colour come from RGB
    60.                 o.GlossColor = _Shininess * gloss.rgb;
    61.                 //Specular is mapped
    62.                 o.Specular = gloss.a;
    63.                 o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
    64.             }
    65.         ENDCG
    66.     }
    67.     Fallback "Diffuse"
    68. }
     
  11. JackRives

    JackRives

    Joined:
    Oct 17, 2009
    Posts:
    111
    michaelvoigt, the shader isn't working properly. I get a bunch of errors such as:

    Code (csharp):
    1. Shader error in 'Specular Colored fast': D3D shader assembly failed with: (49): error X5204: Read of uninitialized component(*) in r0: r/x/0 g/y/1 b/z/2 *a/w/3
    2.  
    3. Shader Assembly: ps_2_0
    4. ; 34 ALU, 4 TEX
    5. dcl_2d s0
    6. dcl_2d s1
    7. dcl_2d s2
    8. dcl_2d s3
    9. def c2, 2.00000000, -1.00000000, 1.00000000, 0.00000000
    10. def c3, 128.00000000, 0, 0, 0
    11. dcl t0.xy
    12. dcl t1.xyz
    13. dcl t2.xyz
    14. dcl t3.xyz
    15. dcl t4
    16. texld r1, t0, s2
    17. texld r3, t0, s0
    18. texldp r6, t4, s3
    19. texld r2, t0, s1
    20. dp3_pp r1.x, t1, t1
    21. mul r2.xyz, r2, c1.x
    22. mov r0.y, r1
    23. mov r0.x, r1.w
    24. mad_pp r4.xy, r0, c2.x, c2.y
    25. mul_pp r0.x, r4.y, r4.y
    26. mad_pp r0.x, -r4, r4, -r0
    27. add_pp r0.x, r0, c2.z
    28. rsq_pp r0.x, r0.x
    29. rcp_pp r4.z, r0.x
    30. rsq_pp r1.x, r1.x
    31. mov_pp r5.xyz, t2
    32. mad_pp r5.xyz, r1.x, t1, r5
    33. dp3_pp r1.x, r5, r5
    34. rsq_pp r1.x, r1.x
    35. mul_pp r0.xyz, r1.x, r5
    36. dp3_pp r1.x, r4, r0
    37. mul_pp r0.x, r2.w, c3
    38. max_pp r1.x, r1, c2.w
    39. pow r5, r1.x, r0.x
    40. dp3_pp r1.x, r4, t2
    41. mov r0.x, r5
    42. mul r2.xyz, r2, c0
    43. mul r2.xyz, r2, r0.x
    44. max_pp r1.x, r1, c2.w
    45. mul r4.xyz, r3, c0
    46. mad r1.xyz, r4, r1.x, r2
    47. mul_pp r0.x, r6, c2
    48. mul r0.xyz, r1, r0.x
    49. mul r1.xyz, r3, t3
    50. add_pp r0.xyz, r0, r1
    51. mov_pp oC0, r0
    52.  
     
  12. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,520
    This should come bundled in the default shaders
     
  13. Chickenlord

    Chickenlord

    Joined:
    May 13, 2011
    Posts:
    381
    You could define your own lighting function without the prepass part, give it a different SurfaceOutput struct and put the specular color inside. However this would force unity to render these materials in forward rendering, causing them to be slow and sometimes also more ugly. but ist possible...

    Edit: Im sorry, didn't realize this topic is quite old.
     
    Last edited: Jul 15, 2011
unityunity