Home Coding, Scripting, Shaders

Advice with Learning Shader Programming

2

Replies

  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    @JackyBoy
    this In.position etc. is C syntax, search for "structs in C" and you will find enough tutorials about this topic.

    @dnc
    Sadly that ShaderFx version expires, so I couldn't worked any longer on that shader. Instead I'm learning hlsl now with notepad from scratch.

    It goes quite well. I've written a simple diffuse lambert shader today, but there a still some thing that I haven't overcome yet.
    Now I want to add phong specular lighting ,but I dont know how to calculate this eyevector / viewvector inside 3ds max ?

    shaders.jpg
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    NBLM wrote: »
    @JackyBoy
    Now I want to add phong specular lighting ,but I dont know how to calculate this eyevector / viewvector inside 3ds max ?

    The view position is simply the world position of the view setup, so taking the view position - world position will give you the vector that points from the world, to the view...

    You can get the view position from a matrix. You can get it by taking the fourth row of the inverse view matrix.

    So to finally get your normalized view vector or eye vector it would simply be
    1. float3 viewVector = normalize(matViewInverse[3].xyz - In.worldPosition.xyz);
    hope this explains it well :)
  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    thx Drew++ for your explanation :)

    During my work on that specular lighting I've found a bug in my shader, whenever
    I put a light between my models the outer one will be lit like the inner one?
    I think a picture describes this better:
    lambertu.jpg
    and my lambert shader:
    1. //////////////////////////////////////////////////////////////
    2. // Data Structs
    3. // input from application
    4. struct application2vertex {
    5. float4 position : POSITION;
    6. float4 normal : NORMAL0;
    7. };
    8.  
    9. // output to fragment program
    10. struct vertex2fragment {
    11. float4 position : POSITION;
    12. float4 worldNormal : NORMAL0;
    13. float4 worldSpacePos : TEXCOORD0;
    14. };
    15.  
    16. //////////////////////////////////////////////////////////////
    17. // Vertex Shader
    18. vertex2fragment vertexShader(application2vertex In)
    19. {
    20. vertex2fragment Out;
    21. Out.worldNormal = mul(In.normal,World); //transform normals from obj space to world
    22. //
    23. //
    24. Out.worldSpacePos = mul(In.position,World);
    25. Out.position = mul(In.position, WorldViewProjection); //transform vertex position from object space to clipspace
    26. return Out;
    27. }
    28.  
    29. //////////////////////////////////////////////////////////////
    30. // Pixel Shader
    31. float4 pixelShader(vertex2fragment In) : COLOR
    32. {
    33. float4 Normal = In.worldNormal;
    34. float3 LightDir = normalize(light1Pos - In.worldSpacePos); //calculate lightvector
    35. float diffuseLight = max(dot(Normal,LightDir),0);
    36. float4 lambert = saturate(DiffuseColor * diffuseLight * light1Color);
    37. float4 final = saturate((AmbientColour * AmbientIntensity) + (lambert));
    38. return final;
    39. }
    40.  
    41. //////////////////////////////////////////////////////////////
    42. // Techniques
    43. technique Base
    44. {
    45. pass one
    46. {
    47. VertexShader = compile vs_3_0 vertexShader();
    48. ZEnable = true;
    49. ZWriteEnable = true;
    50. ZFunc = LessEqual;
    51. CullMode = none;
    52. AlphaBlendEnable = false;
    53. AlphaTestEnable = false;
    54. PixelShader = compile ps_3_0 pixelShader();
    55. }
    56. }
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Trying to jump in quick to show off my recently learnt knowledge :p

    But is it because in your Pixel shader you have your lightDir calculated with "light1pos" to work out the light vector, when in fact you need multiple passes for it to work out each individual light and blend? Or at least have LightDir2 with light2pos if that's possible?
  • dnc
    It may be worth making sure your light is in world space before you get the direction. You could do this by doing:

    light1Pos = mul(light1Pos, World);
  • cw
    Offline / Send Message
    cw polycounter lvl 17
    NBLM wrote: »
    thx Drew++ for your explanation :)

    During my work on that specular lighting I've found a bug in my shader, whenever
    I put a light between my models the outer one will be lit like the inner one?
    I think a picture describes this better:
    lambertu.jpg
    and my lambert shader:
    1. //////////////////////////////////////////////////////////////
    2. // Data Structs
    3. // input from application
    4. struct application2vertex {
    5. float4 position : POSITION;
    6. float4 normal : NORMAL0;
    7. };
    8.  
    9. // output to fragment program
    10. struct vertex2fragment {
    11. float4 position : POSITION;
    12. float4 worldNormal : NORMAL0;
    13. float4 worldSpacePos : TEXCOORD0;
    14. };
    15.  
    16. //////////////////////////////////////////////////////////////
    17. // Vertex Shader
    18. vertex2fragment vertexShader(application2vertex In)
    19. {
    20. vertex2fragment Out;
    21. Out.worldNormal = mul(In.normal,World); //transform normals from obj space to world
    22. //
    23. //
    24. Out.worldSpacePos = mul(In.position,World);
    25. Out.position = mul(In.position, WorldViewProjection); //transform vertex position from object space to clipspace
    26. return Out;
    27. }
    28.  
    29. //////////////////////////////////////////////////////////////
    30. // Pixel Shader
    31. float4 pixelShader(vertex2fragment In) : COLOR
    32. {
    33. float4 Normal = In.worldNormal;
    34. float3 LightDir = normalize(light1Pos - In.worldSpacePos); //calculate lightvector
    35. float diffuseLight = max(dot(Normal,LightDir),0);
    36. float4 lambert = saturate(DiffuseColor * diffuseLight * light1Color);
    37. float4 final = saturate((AmbientColour * AmbientIntensity) + (lambert));
    38. return final;
    39. }
    40.  
    41. //////////////////////////////////////////////////////////////
    42. // Techniques
    43. technique Base
    44. {
    45. pass one
    46. {
    47. VertexShader = compile vs_3_0 vertexShader();
    48. ZEnable = true;
    49. ZWriteEnable = true;
    50. ZFunc = LessEqual;
    51. CullMode = none;
    52. AlphaBlendEnable = false;
    53. AlphaTestEnable = false;
    54. PixelShader = compile ps_3_0 pixelShader();
    55. }
    56. }

    The lambert looks ok, where is the code for your specular pls?
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    Specular is a lot more open in how you calculate it. Try not to get lost in all the crazy models and functions out there at the start (like Cook-Torrance and so on), some people tend to obsess a bit over that, I think it doesn't matter as long as it looks good.

    Also, here's my function from my shader:
    1. [COLOR=SeaGreen]//seperate specular calculation to make my life easier coding this thing
    2. //color and masking is NOT done here; this is just for pure, raw specular calculation
    3. //thanks to http://wiki.gamedev.net/index.php/D3DBook:(Lighting)_Blinn-Phong for the very clean and understandable explanation[/COLOR]
    4. float4 blinnspecular(float3 normal, float3 lightvec, float3 eyevec, float4 glossiness)
    5. {
    6. normal = normalize(normal);
    7. lightvec = normalize(lightvec);
    8. eyevec = normalize(eyevec);
    9. float3 halfvector = normalize(eyevec+lightvec); /[COLOR=SeaGreen]/add eye and light together for half vector (Blinn)[/COLOR]
    10. float4 specular;
    11. specular = dot(halfvector, normal); [COLOR=SeaGreen]//dot between half and normal (Blinn)[/COLOR]
    12. specular = float4( pow(specular.r, glossiness.r),pow(specular.g, glossiness.g), pow(specular.b, glossiness.b), pow(specular.a, glossiness.a)); [COLOR=SeaGreen]//power specular to glossiness to sharpen highlight[/COLOR]
    13. specular *= saturate(dot(normal,lightvec) * 4);[COLOR=SeaGreen] //fix for Specular through surface bug. what this does is just make sure no specular happens on unlit parts. the multiplier works as a bias[/COLOR]
    14. return specular;
    15. }
    16.  

    I tried to make it generic enough: no specmap required yet, can be used for any light. I just run this 3 times to calculate every light's spec on the model.
    Take note of the final line: this is a hacky little fix for a bug where spec appears through surfaces. You won't find this in any official book or formula: I just write stuff that works for me. what it does is just quickly check if light actually reaches the area where specular is calculated, and makes sure no spec will be visible if the area is unlit.
  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    LoL this makes me mad. I dont know if it is this hlsl compiler or max but it
    is crazy. I've put my code into some fancy functions like Xoliul did, to get a
    better overview. But instead working like before as I would expect it, it does not

    I've got no compile errors or anything else, but my specular calculation doesn't work now. I cant see the problem here.

    Its the same code as before , just put into functions.
    Here is my full code:
    1. /***********************************************************
    2.  
    3. Created by O Cullen -> hacked by NBLM
    4.  
    5. HLSL Simple Base Shader - This shader contains usuall matrices and descriptions for more complex shaders, but only displays a basic colour output.
    6.  
    7. Lambert - blinn phong shader
    8.  
    9. **************************************************************/
    10.  
    11.  
    12. //////////////////////////////////////////////////////////////
    13. // Matrices
    14. float4x4 WorldViewProjection : WorldViewProjection < string UIWidget = "None"; >;
    15. float4x4 WorldInverseTranspose : WorldInverseTranspose < string UIWidget = "None"; >;
    16. float4x4 ViewInverse : ViewInverse < string UIWidget = "None"; >;
    17. float4x4 World : World < string UIWidget = "None"; >;
    18. float4x4 WorldInverse : WorldInverse < string UIWidget = "None"; >;
    19.  
    20. // Maya Description
    21. string description = "Basic HLSL Shader";
    22.  
    23. //////////////////////////////////////////////////////////////
    24. // Parameters
    25.  
    26. //AmbientColour
    27. float4 AmbientColour
    28. <
    29. string UIWidget = "Color";
    30. string UIName = "Ambient Colour";
    31. > = {0.6f, 0.6f, 0.6f, 1.0f};
    32.  
    33. float AmbientIntensity
    34. <
    35. string UIType = "FloatSpinner";
    36. float UIMin = 0.0;
    37. float UIMax = 20.0;
    38. float UIStep = 0.1;
    39. string UIName = "Ambient Intensity";
    40. > = 0.0;
    41.  
    42. //diffuse
    43. float4 DiffuseColor
    44. <
    45. string UIWidget = "Color";
    46. string UIName = "Diffuse Colour";
    47. > = {0.6f, 0.6f, 0.6f, 1.0f};
    48.  
    49. //specular
    50. float shininess = 12.0;
    51. float4 SpecularColor = float4(1, 1, 1, 1);
    52. float SpecularPower = 1;
    53.  
    54.  
    55. bool bUseExternalFunctions
    56. <
    57. string gui = "slider";
    58. string UIName = "Use ExternalFunctions";
    59. > = false;
    60.  
    61. //////////////////////////////////////////////////////////////
    62. // light info COPY FROM SHADERFX FILE
    63. /*
    64. float3 light1Dir : Direction
    65. <
    66. string UIName = "Light 1 Direction";
    67. string Object = "TargetLight";
    68. string Space = "World";
    69. int refID = 1;
    70. > = {100.0f, 100.0f, 100.0f};
    71. */
    72. float3 light1Pos : POSITION
    73. <
    74. string UIName = "Light 1 Position";
    75. string Object = "PointLight";
    76. string Space = "World";
    77. int refID = 1;
    78. > = {100.0f, 100.0f, 100.0f};
    79.  
    80. float4 light1Color : LIGHTCOLOR <int LightRef = 1; string UIWidget = "None"; > = { 0.0f, 0.0f, 0.0f, 0.0f};
    81. float4 light1Attenuation : Attenuation <int LightRef = 1; string UIWidget = "None"; > = { 20.0f, 30.0f, 0.0f, 100.0f};
    82. float light1Hotspot : HotSpot <int LightRef = 1; string UIWidget = "None"; > = { 43.0f };
    83. float light1Falloff : FallOff <int LightRef = 1; string UIWidget = "None"; > = { 45.0f };
    84.  
    85.  
    86. //////////////////////////////////////////////////////////////
    87. // Data Structs
    88. // input from application
    89. struct application2vertex {
    90. float4 position : POSITION;
    91. float4 normal : NORMAL0;
    92. };
    93.  
    94. // output to fragment program
    95. struct vertex2fragment {
    96. float4 position : POSITION;
    97. float4 worldNormal : NORMAL0;
    98. float4 worldSpacePos : TEXCOORD0;
    99. float3 viewVec : TEXCOORD1;
    100. };
    101.  
    102.  
    103. float lambert(float4 Normal, float3 LightVec){
    104. Normal = normalize(Normal);
    105. LightVec = normalize(LightVec);
    106. float l = max(dot(Normal,LightVec),0);
    107. return l;
    108. }
    109.  
    110. float blinn(float4 Normal, float3 viewVec, float3 lightVec, float Shininess){
    111. Normal = normalize(Normal);
    112. viewVec = normalize(viewVec);
    113. lightVec = normalize(lightVec);
    114. float3 Halfway = normalize(lightVec + viewVec);
    115. float b = pow(max(dot(Normal,Halfway),0),Shininess);
    116. return b;
    117. }
    118.  
    119.  
    120.  
    121. //////////////////////////////////////////////////////////////
    122. // Vertex Shader
    123. vertex2fragment vertexShader(application2vertex In)
    124. {
    125. vertex2fragment Out;
    126. Out.worldNormal = mul(In.normal,World); //transform normals from obj space to world
    127. //
    128. //
    129. Out.worldSpacePos = mul(In.position,World);
    130. Out.viewVec = ViewInverse[3].xyz - In.position.xyz;
    131. Out.position = mul(In.position, WorldViewProjection); //transform vertex position from object space to clipspace
    132. return Out;
    133. }
    134.  
    135. //////////////////////////////////////////////////////////////
    136. // Pixel Shader
    137. float4 pixelShader(vertex2fragment In) : COLOR
    138. {
    139. if(bUseExternalFunctions){
    140. /////////////////WORKS NOT//////////////////////////////////////////////////////////////////////////////
    141. float3 LightVec = light1Pos - In.worldSpacePos;
    142. //Diffuse lighting = for 1 omni light only atm.
    143. float diffLight = lambert(In.worldNormal,LightVec);
    144. float4 diffuse = saturate(DiffuseColor * light1Color * diffLight);
    145. //Specular lighting
    146. float specLight = blinn(In.worldNormal,In.viewVec,LightVec,shininess)*4;
    147. if(diffLight <= 0.0) specLight = 0.0; //pixel that are not illuminated should not get a specular highlight
    148. float4 specular = saturate(SpecularPower * SpecularColor * light1Color * specLight);
    149. float4 final = saturate((AmbientColour * AmbientIntensity) + (diffuse) + specular);
    150. return final;
    151. /////////////////WORKS FINE//////////////////////////////////////////////////////////////////////////////
    152. }
    153. else{
    154. float4 Normal = In.worldNormal;
    155.  
    156. //Diffuse lighting
    157. float3 LightDir = normalize(light1Pos - In.worldSpacePos); //calculate lightvector
    158. float diffuseLight = max(dot(Normal,LightDir),0);
    159. float4 lambert1 = saturate(DiffuseColor * diffuseLight * light1Color);
    160. ///Specular lighting
    161. float3 viewDir = normalize(In.viewVec);
    162. float3 Halfway = normalize(LightDir + viewDir);
    163. float specularLight = pow((max(dot(Normal,Halfway),0)),shininess);
    164. if(diffuseLight <= 0.0) specularLight = 0.0; //pixel that are not illuminated should not get a specular highlight
    165. float4 blinn = saturate(SpecularPower * SpecularColor * specularLight * light1Color);
    166. float4 final = saturate((AmbientColour * AmbientIntensity) + (lambert1) + (blinn));
    167. return final;
    168. }
    169. }
    170.  
    171. //////////////////////////////////////////////////////////////
    172. // Techniques
    173. technique Base
    174. {
    175. pass one
    176. {
    177. VertexShader = compile vs_3_0 vertexShader();
    178. ZEnable = true;
    179. ZWriteEnable = true;
    180. ZFunc = LessEqual;
    181. CullMode = none;
    182. AlphaBlendEnable = false;
    183. AlphaTestEnable = false;
    184. PixelShader = compile ps_3_0 pixelShader();
    185. }
    186. }
    you can switch between the old and the new version with the "use external functions" flag in 3ds max
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    I just tried your code, none of it works fine, both external and non external are doing really weird things... as in really, really broken lighting.
    I suggest you fix that first before bothering with those functions. it's some sort of matrix/space problem as lighting seems offset and dependent on distance.

    Found your problem: you are using a float4 as input for the normal by the application. That means a W-component is created for it as well, which you end up using in all your calculations. That W-component messes up pretty much everything.
  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    awesome Xoliul! Big thx :) ...now all these weird bugs are gone!
  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    I've now added normalmap,glossmap-support and light attenuation to my shader, and fixed some errors.
    Its still not the prettiest, but I think I've learned enough to move further.
    I can post my shader here, if anyone is interested ?
    myshader.jpg


    I'm now learning cubemaps and how to use them for reflection/refractions. And
    I'm stuck here. I dont know what I'm doing wrong that my shader looks like this (left side):
    cubemapproblem.jpg
    My code:
    1. //////////////////////////////////////////////////////////////
    2. // Matrices
    3. float4x4 WorldViewProjection : WorldViewProjection < string UIWidget = "None"; >;
    4. float4x4 WorldInverseTranspose : WorldInverseTranspose < string UIWidget = "None"; >;
    5. float4x4 ViewInverse : ViewInverse < string UIWidget = "None"; >;
    6. float4x4 World : World < string UIWidget = "None"; >;
    7. float4x4 WorldInverse : WorldInverse < string UIWidget = "None"; >;
    8.  
    9. // Maya Description
    10. string description = "Basic HLSL Shader";
    11.  
    12. //////////////////////////////////////////////////////////////
    13. // Parameters
    14.  
    15. //AMBIENTCOLOUR
    16. float4 AmbientColour
    17. <
    18. string UIWidget = "Color";
    19. string UIName = "Ambient Colour";
    20. > = {0.6f, 0.6f, 0.6f, 1.0f};
    21.  
    22. float AmbientIntensity
    23. <
    24. string UIType = "FloatSpinner";
    25. float UIMin = 0.0;
    26. float UIMax = 20.0;
    27. float UIStep = 0.1;
    28. string UIName = "Ambient Intensity";
    29. > = 0.0;
    30.  
    31. //NORMAL
    32. bool bUseNormalmap
    33. <
    34. string gui = "slider";
    35. string UIName = "Use Normal Map";
    36. > = false;
    37.  
    38. texture normalMap : NORMALMAP
    39. <
    40. string name ="";
    41. string UIName = " Normal Map";
    42. string ResourceType = "2D";
    43.  
    44. >;
    45.  
    46. sampler2D normalSampler = sampler_state
    47. {
    48. Texture = <normalMap>;
    49. MinFilter=LINEAR;
    50. MagFilter=LINEAR;
    51. MipFilter=LINEAR;
    52. MipLODBias=-1;
    53. AddressU = WRAP;
    54. AddressV = WRAP;
    55. };
    56. bool bFlipY
    57. <
    58. string gui = "slider";
    59. string UIName = "Flip Y";
    60. > = false;
    61.  
    62. //CUBEMAP
    63. bool bUseCube
    64. <
    65. string gui = "slider";
    66. string UIName = "Use Cube Map";
    67. > = false;
    68.  
    69. texture cubeMap
    70. <
    71. string name ="";
    72. string UIName = " Cube Map";
    73. string ResourceType = "Cube";
    74.  
    75. >;
    76.  
    77. samplerCUBE cubeSampler = sampler_state
    78. {
    79. Texture = <cubeMap>;
    80. MinFilter=LINEAR;
    81. MagFilter=LINEAR;
    82. MipFilter=LINEAR;
    83. MipLODBias=0;
    84. AddressU = WRAP;
    85. AddressV = WRAP;
    86. };
    87.  
    88. float blur
    89. <
    90. string UIType = "FloatSpinner";
    91. float UIMin = 0.0;
    92. float UIMax = 8.0;
    93. float UIStep = 0.1;
    94. string UIName = "Cubemap blur";
    95. > = 0.0;
    96.  
    97. //////////////////////////////////////////////////////////////
    98. // light info
    99. float3 light1Pos : POSITION
    100. <
    101. string UIName = "Light 1 Position";
    102. string Object = "PointLight";
    103. string Space = "World";
    104. int refID = 1;
    105. > = {100.0f, 100.0f, 100.0f};
    106.  
    107. float4 light1Color : LIGHTCOLOR <int LightRef = 1; string UIWidget = "None"; > = { 0.0f, 0.0f, 0.0f, 0.0f};
    108.  
    109. //////////////////////////////////////////////////////////////
    110. // Data Structs
    111. // input from application
    112. struct application2vertex {
    113. float4 position : POSITION;
    114. float3 normal : NORMAL;
    115. float3 tangent : TANGENT;
    116. float3 binormal : BINORMAL;
    117. float2 texCoord0 : TEXCOORD0;
    118.  
    119. };
    120.  
    121. // output to fragment program
    122. struct vertex2fragment {
    123. float4 position : POSITION;
    124. float2 otexCoord0 : TEXCOORD0;
    125. float3 worldNormal : TEXCOORD1;
    126. float3 worldTangent : TEXCOORD2;
    127. float3 worldBinormal : TEXCOORD3;
    128. float3 viewVec : TEXCOORD4;
    129. float3 lightVec : TEXCOORD5;
    130. };
    131.  
    132.  
    133. //////////////////////////////////////////////////////////////
    134. // Vertex Shader
    135. vertex2fragment vertexShader(application2vertex In)
    136. {
    137. vertex2fragment Out;
    138. //TEXTURES
    139. Out.otexCoord0 = In.texCoord0;
    140. //VECTORS
    141. Out.worldNormal = mul(In.normal,WorldInverseTranspose).xyz; //transform normals from obj space to world
    142. Out.worldTangent = mul(In.tangent,WorldInverseTranspose).xyz;
    143. Out.worldBinormal = mul(In.binormal,WorldInverseTranspose).xyz;
    144. float3 worldSpacePos = mul(In.position,World);
    145. Out.lightVec = light1Pos - worldSpacePos;
    146. Out.viewVec = ViewInverse[3].xyz - worldSpacePos;
    147. //POSITION
    148. Out.position = mul(In.position, WorldViewProjection); //transform vertex position from object space to clipspace
    149. return Out;
    150. }
    151.  
    152. //////////////////////////////////////////////////////////////
    153. // Pixel Shader
    154. float4 pixelShader(vertex2fragment In) : COLOR
    155. {
    156. float3 LightVec = In.lightVec;
    157. float3 Normal = In.worldNormal;
    158. float3 Binormal = In.worldBinormal;
    159. float3 Tangent = In.worldTangent;
    160. float4 diffuse = saturate(0.5);
    161. //Normal map
    162. if(bUseNormalmap){
    163. float3 normalmap = tex2D(normalSampler,In.otexCoord0.xy);
    164. normalmap = (2*normalmap)-1; // extend the range from 0 - 1 to (-1) - 1
    165. if(!bFlipY) normalmap.g = -normalmap.g;
    166. normalmap = (Normal * normalmap.z) + (Binormal * normalmap.x) + (Tangent * normalmap.y); //modify your vertex normals with those stored in your normalmap
    167. Normal = normalize(normalmap.xyz);
    168. }
    169. //cubemap reflection
    170. if(bUseCube){
    171. float3 reflection = reflect(In.viewVec, Normal);
    172. float4 cubemap = texCUBElod(cubeSampler, float4(reflection,blur));
    173. diffuse = cubemap;
    174. }
    175.  
    176. float4 final = saturate((AmbientColour * AmbientIntensity) + (diffuse));
    177. return final;
    178. }
    179.  
    180. //////////////////////////////////////////////////////////////
    181. // Techniques
    182. technique Base
    183. {
    184. pass one
    185. {
    186. VertexShader = compile vs_3_0 vertexShader();
    187. ZEnable = true;
    188. ZWriteEnable = true;
    189. ZFunc = LessEqual;
    190. CullMode = cw;
    191. AlphaBlendEnable = false;
    192. AlphaTestEnable = false;
    193. PixelShader = compile ps_3_0 pixelShader();
    194. }
    195. }
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    There are a couple of things you probably should change here. :)

    -First thing is when you are transforming your tangents, normals, and binormals into 'world space'. They naturally are in world space, so simply pass them to your fragment shader straight from the application. For example:
    1. Out.worldNormal = In.normal;
    2. Out.worldTangent = In.tangent;
    3. Out.worldBinormal = In.binormal;
    -Second thing is when using a normal map, you need to swizzle your normal map around before using it. So if a normal map is being used your normal map should become:
    1. normalmap = float3(normalmap .y, normalmap .x, normalmap .z);
    You would also need to do your "bFlipY" before this operation..

    -Third thing is that cubemaps need a different normal lookup, since 3DS Max is Z up... so your normal strictly for cubemaps becomes:
    1. Normal = float3(Normal.x, Normal.z, Normal.y);
    y and z are simply swapped :)


    One more thing... Normalize your variables such as view vec, normals, etc.

    :D
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Hey all, learning is still going strong! But wondered if anyone could explain a little more to me about lights.

    The shaders I have been tinkering with use 1 or 2 lights, but what I want to know is how would this differ if you were going to use it in a game engine, or even more lights in max (I don't need actual code, just the theory behind it). Do you have to code a "light1pos", "light2pos", "light3pos"... for every light you MIGHT have in the scene, or do you do it in a funky way where the shader realises there is another light so does a lightNpos + 1?
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    Good question Jacky.
    As you see, you need to explicitly provide code for every additional light, it adds performance cost, and if you want to optimize in case there are fewer lights, you get branching as an additional issue: doing IF's in shaders is not as straightforward as regular cpu code, shaders are meant for the GPU to thunder through while doing the same code with just different variables: changing the code (like with an IF or when switching shader file) comes with a cost.

    The solution to this lies with the engine, and adjusting your shaders accordingly. First of all, your engine needs to render either Forward or deferred. Forward rendering is what my shader is and what you guys are practicing. Simply a shader per object, working by itself; light code is done per object, you can have vastly different shaders for different objects. Deferred shading is completely different and is more like a post-effect. The renderer will do a number of passes, for example diffuse color, normals, specular, reflection, emmissive, all with standardized shaders for all objects. then a final pass will composite everything together. The advantage is that you're calculating your lights only for that single, final image, and not for every object. Much better performance wise, but limited in other ways (much harder to do nodebased unlimited shaders like Unreal). Deferred is a newer technique because GPU's couldn't handle so many rendertargets until about 8 years ago.

    There's also different approaches, I think unreal does forward shading most of the time (lightmapped objects are forward shaded), and it will do one single dynamic light (the dominant light, like the sun). If there are any additional, dynamic lights (like explosions, flashing lights) they will be done in an additional deferred pass that gets composited on top.
    Frostbite and Cryengine are both fully deferred I think. Frostbite has nodebased shaders, Cryengine limits you to a set of pre-built shaders.

    I guess to conclude I could say this:
    the way we are coding shaders is actually a bit inefficient and not suited for integration in a large engine, there's so much more that comes into play there. This stuff could work fine in a simple, limited "engine" or framework though, but you'd be limited to a set amount of lights (code picks the 3 closest ones for example then).
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Awesome answer Xoliul, thank you.

    Ok, so I had a little look at how the differed works, and as far as I can tell you would still do it in HLSL but you need to add in a gBuffer (Or MRTs?). Then instead of doing the lighting in the pixel shader you store the various scene elements in the gBuffer then composite them in the pixel shader at the end? Aqain, no code need, just want to get the theory sorted in my head :)

    Also, would this be a good thing to move onto next about learning shaders? Is that "the way the industry is heading" or is it just another technique and I shouldn't worry too much about it?
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    You would do some of the stuff in HLSL, but things will be a lot more spread out. the majority happens in the actual render framework code of the application.
    I've never wirtten this stuff before, but to say it simplified you would have a few simple shaders (or one with multiple techniques) for the passes: one that purely returns diffuse, one that returns normals, one for spec, etc... These would each render to a rendertarget or gBuffer, though creating, handling and storing these is done outside of HLSL. HLSL/a shader is pretty "dumb" when viewed in the total render pipeline: it doesn't do much else than crunch pixels (even shadows for example require application code outside of HLSL).
    Once you have stored all the passes in Gbuffers, you pass them to another composite-shader as textures. This composite shader then does another pass of crunching to composite the final image.

    I wouldn't recommend doing that, really, unless you're prepared to write your own render framework to surround it. there's no point in "practising" that unless you also have access to the main application code surrounding it, as they are so closely tied together.
    Not to say that is impossible, but since there is no existing, easy frame work for it (like max for forward shading), you'd need get into some heavier programming.

    I have to be honest: despite doing shaders in my spare time and having a good understanding, I never actually write shader code for the game at work (though I have written "tool" shaders for Max). The engines are too complex (Unreal and Frostbite) and provide a good node based interface, so there is no need. If you work with a simpler engine (Unity too I think) or some indie project then there is a much larger possiblity. You still have to work closely with a programmer though.

    if you want a higher level overview of what I think is a good path:

    get up to a level where you can easily write a mutlifunctional, standard shading with some bells and whistles like normal mapping and cubemap reflections.
    You could try some post-effect shaders in max. In theory simpler than object shaders (you're just working on a single image, kinda like image editing), the crappy interface in max makes it harder. Interesting nonetheless, i wrote a bunch of them a few years ago, I should put them up on my site again.
    You could try some special shaders that are more specific in purpose. Stuff I've written/seen:
    Vertex colored blending, or world-direction based (like terrian painting or snow on top of things).
    Mixed object/post shader to do actual glow outside of model edges (with different techniques and passes).
    Front/top/back/side modelsheet projection onto blockout model.
    UV-distortion visualisation with ddX() and ddY(). Complicated shit already!
    Procedural noise effects in worldspace, try a few different noises like perlin, Voronoi, etc... This stuff gets really, really complicated ;)

    All fun stuff! If you manage to do all of this, I'd say you got as far as "mastering shaders" goes for an artist. Any further (like coding a deferred pipeline) and you move into graphics programmer territory...
  • haiddasalami
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Got an opportunity to work on a deferred pipeline and most of the shaders were pretty self explanatory and the skills I learned from HLSL/CGFX was just as easy to port over. Definitely agree with the path Xoliul laid out. Sounds like a good flow and you touch a bunch of stuff in the shaders/graphics programming realm.
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Thanks for the detailed response, again :)

    I have a much better idea of where shaders are fitting into the overall pipeline. Also good to know of the scope that I will be learning. That is a great list of shaders to try and create too, I shall definitely be using something along those lines for "further learning" (once I figure out what some of them mean of course... :poly124: )

    I have got to a point where I just have a Textured normal map shader working (even got a little ahead of myself and threw in a cheeky array), but the only way I can get the alpha to work correctly is by having Out.a = 1.0f; at the end. If I just have it return Diffuse + Ambient, it looks correct but the light also seems to act as some kind of alpha and where it is lit it makes it transparent?

    So I wondered if someone could have a little look and see why it is doing that? Another point is float3 and float4, I can't figure out when to use them. I have to swap between them and back again when writing my code, but that might just be down to inexperience.

    Also if you could look at the general state of my code, see if I am doing things "correctly", or if anything is sticking out where it shouldn't be.

    Thanks for all the help!
    1. string ParamID = "0x003";
    2.  
    3. float4x4 wvp : WORLDVIEWPROJ < string UIWidget = "None"; >;
    4. float4x4 WorldInverseTranspose : WORLDINVERSETRANSPOSE < string UIWidget = "None"; >;
    5. float4x4 ViewInverse : VIEWINVERSE < string UIWidget = "None"; >;
    6. float4x4 World : WORLD < string UIWidget = "None"; >;
    7.  
    8. //////////////////////////////////////////////////////////////
    9. // Parameters section
    10. float4 shaderColor : DIFFUSE
    11. <
    12. string UIName = "Shader Colour";
    13. string UIWidget = "ColorSwatch";
    14. > = {1.0f, 1.0f, 1.0f, 1.0f};
    15.  
    16.  
    17.  
    18. float4 ambientColor : AMBIENT
    19. <
    20. string UIName = "Ambient Colour";
    21. string UIWidget = "ColorSwatch";
    22. > = {0.15f, 0.15f, 0.15f, 1.0f};
    23.  
    24.  
    25.  
    26. texture diffuseMap : DiffuseMap
    27. <
    28. string name = "default_color.dds";
    29. string UIName = "Diffuse Texture";
    30. string TextureType = "2D";
    31. >;
    32.  
    33. texture normalMap : NormalMap
    34. <
    35. string name = "default_normal.dds";
    36. string UIName = "Normal Map";
    37. string TextureType = "2D";
    38. >;
    39.  
    40. sampler2D diffuseMapSampler = sampler_state
    41. {
    42. Texture = <diffuseMap>;
    43. MinFilter = Linear;
    44. MagFilter = Linear;
    45. MipFilter = Linear;
    46. };
    47.  
    48.  
    49. sampler2D normalMapSampler = sampler_state
    50. {
    51. Texture = <normalMap>;
    52. MinFilter = Linear;
    53. MagFilter = Linear;
    54. MipFilter = Linear;
    55. };
    56.  
    57. //////////////////////////////////////////////////////////////
    58. // Lights section
    59.  
    60. float4 lightPos : POSITION
    61. <
    62. string UIName = "Light Position";
    63. string Object = "PointLight";
    64. string Space = "World";
    65. int RefID = 0;
    66. > = {100.0f, 100.0f, 100.0f, 0.0f};
    67.  
    68. float4 lightColor : LIGHTCOLOR
    69. <
    70. int LightRef = 0;
    71. > = { 1.0f, 1.0f, 1.0f, 0.0f };
    72.  
    73. //////////////////////////////////////////////////////////////
    74. // Structs section
    75.  
    76. // input from application (Application 2 Vertex) These are "filled" from the app, you can get data from these
    77. struct a2v{
    78. float4 position : POSITION;
    79. float2 texCoord : TEXCOORD0;
    80. float4 normal : NORMAL; //float4 for multiplication purposes in 4x4 matrices
    81. float4 binormal : BINORMAL;
    82. float4 tangent : TANGENT;
    83. };
    84.  
    85. // output to fragment program (Vertex 2 Fragment) These are "filled" from the Vertex shader
    86. struct v2f
    87. {
    88. float4 position : POSITION;
    89. float2 texCoord : TEXCOORD0;
    90. float3 lightVec : TEXCOORD1;
    91. float3 worldNormal : TEXCOORD2;
    92. float3 worldBinormal: TEXCOORD3;
    93. float3 worldTangent : TEXCOORD4;
    94. };
    95.  
    96. //////////////////////////////////////////////////////////////
    97. // Vertex Shader
    98. v2f vShader(a2v In)
    99. {
    100. v2f Out;
    101. Out.worldNormal = mul(In.normal, WorldInverseTranspose).xyz;
    102. Out.worldBinormal = mul(In.binormal, WorldInverseTranspose).xyz;
    103. Out.worldTangent = mul(In.tangent, WorldInverseTranspose).xyz;
    104. float3 worldSpacePos = mul(In.position, World).xyz; //transform vert pos to world space
    105. Out.lightVec = lightPos.xyz - worldSpacePos;
    106. Out.texCoord = In.texCoord;
    107. Out.position = mul(In.position, wvp);
    108. return Out;
    109. }
    110.  
    111. //////////////////////////////////////////////////////////////
    112. // Pixel Shader
    113. float4 pShader(v2f In) : COLOR
    114. {
    115. //float4 outColor;
    116. float4 Out;
    117. float4 ColorTexture = tex2D(diffuseMapSampler, In.texCoord);
    118. float3 normal = tex2D(normalMapSampler, In.texCoord).rgb * 2 - 1;
    119. float3x3 objToTangentSpace;
    120. objToTangentSpace[0] = normalize(In.worldBinormal);
    121. objToTangentSpace[1] = normalize(-In.worldTangent);
    122. objToTangentSpace[2] = normalize(In.worldNormal);
    123. float3 N = mul(normal, objToTangentSpace);
    124. N = normalize(N);
    125. float3 L = normalize(In.lightVec); //makes lightVec length of 1
    126. float4 Ambient = ambientColor * ColorTexture;
    127. float4 light = saturate(dot(N,L));
    128. float4 Diffuse = ColorTexture * light;
    129. Out.rgb = Diffuse.rgb + Ambient.rgb;
    130. Out.a = 1.0f;
    131. return Out;
    132. }
    133.  
    134. //////////////////////////////////////////////////////////////
    135. // Techniques
    136. technique Simple
    137. {
    138. pass one
    139. {
    140. VertexShader = compile vs_3_0 vShader();
    141. PixelShader = compile ps_3_0 pShader();
    142. }
    143. }
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    Hey jacky.

    I see some weirdness:
    1. float4 light = saturate(dot(N,L));
    2. float4 Diffuse = ColorTexture * light;
    a dot product always returns a single float, not a vector. So what happens when you put this into a float 4, is that every component (r, g, b and a) get assigned this value. It will look grey scale with that same grey scale value in the alpha. Your alpha is just coming along all the way till the end then: the color texture's alpha is 1, this gets multiplied by your light so it becomes the same value.

    What you're doing at the end, explicitly setting it to 1.0f, is a good thing though. I do that too: alpha tends to be a simple value. I just do an If-statement that sets Out.a to either DiffuseTexture.a or 1.0f.

    Now regarding when to go for float3 or float4:

    float 3 should be your default in almost every case for actual vector values. When doing vector calculations, the W/A component tends to act as a length of sorts, often messing up calculations (like what was happening to NBLM).
    Only go for float 4 when you explicitly need it if it's a color value.
    Really, the only times where you should use float4 is your actual output pixel value and whenever you want to sample a texture with alpha.
    Admittedly, this often tends to get annoying since you can't perform any operations with a float3 and float4 together and you end up using float 4 for every color. The solution is to do what you do: override at the end to make sure.
  • NBLM
    Offline / Send Message
    NBLM polycounter lvl 13
    I've found my problem. I've forgotten to assign a compiler :shifty:. It works now with:
    1. string ParamID = "0x003";
    I've also updated my code with your suggestions, thx Drew++!
    Just that point with not transforming the normals is not 100% true. If you
    just pass the normals,binormals and tangents to you pixelshader they may work as before,
    but they are static then and get not affected by rotation.
    shadertest7749.jpg
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    That would be "object-space" and not world-space.
  • Drew++
    Offline / Send Message
    Drew++ polycounter lvl 14
    NBLM wrote: »
    I've found my problem. I've forgotten to assign a compiler :shifty:. It works now with:
    1. string ParamID = "0x003";
    I've also updated my code with your suggestions, thx Drew++!
    Just that point with not transforming the normals is not 100% true. If you
    just pass the normals,binormals and tangents to you pixelshader they may work as before,
    but they are static then and get not affected by rotation.
    shadertest7749.jpg

    Ahh right... That would be object space. I'm used to working with an engine that handles this for me :V

    So just do this
    1. Out.worldNormal = mul(In.normal, matWorldInverseTranspose);
    2. Out.worldTangent = mul(In.tangent, matWorldInverseTranspose);
    3. Out.worldBinormal = mul(In.binormal, matWorldInverseTranspose);
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Hey all, trying to get Spec and gloss working now. I have my gloss in the aplpha of my spec channel. They look like this:

    2GYTi.jpg

    But it seems that my specular is always getting blown out.

    Here it is with the gloss spinner at 100:

    2GYWR.jpg

    And here it is at 0:

    2GYYh.jpg

    Here is my code:
    1. string ParamID = "0x003";
    2.  
    3. float4x4 wvp : WORLDVIEWPROJ < string UIWidget = "None"; >;
    4. float4x4 WorldInverseTranspose : WORLDINVERSETRANSPOSE < string UIWidget = "None"; >;
    5. float4x4 ViewInverse : VIEWINVERSE < string UIWidget = "None"; >;
    6. float4x4 World : WORLD < string UIWidget = "None"; >;
    7.  
    8. //////////////////////////////////////////////////////////////
    9. // Parameters section
    10.  
    11. float4 specularColor : SPECULAR
    12. <
    13. string UIName = "Specular Colour";
    14. //string UIWidget = "ColorSwatch";
    15. > = {0.15f, 0.15f, 0.15f, 1.0f};
    16.  
    17.  
    18. float4 ambientColor : AMBIENT
    19. <
    20. string UIName = "Ambient Colour";
    21. //string UIWidget = "ColorSwatch";
    22. > = {0.15f, 0.15f, 0.15f, 1.0f};
    23.  
    24. float glossiness
    25. <
    26. string UIName = "Glossiness Level";
    27. string UIType = "FloatSpinner";
    28. float UIMin = 0.0f;
    29. float UIMax = 100.0f;
    30. float UIStep = 0.05;
    31. > = 25.0f;
    32.  
    33.  
    34. texture diffuseMap : DiffuseMap
    35. <
    36. string name = "default_color.dds";
    37. string UIName = "Diffuse Texture";
    38. string TextureType = "2D";
    39. >;
    40.  
    41. texture normalMap : NormalMap
    42. <
    43. string name = "default_normal.dds";
    44. string UIName = "Normal Map";
    45. string TextureType = "2D";
    46. >;
    47.  
    48. bool bFlipGreenChannel
    49. <
    50. string gui = "slider";
    51. string UIName = "Flip Green";
    52. > = true;
    53.  
    54. texture specularMap : SpecularMap
    55. <
    56. string name = "default_color.dds";
    57. string UIName = "Specular Texture";
    58. string TextureType = "2D";
    59. >;
    60.  
    61. sampler2D diffuseMapSampler = sampler_state
    62. {
    63. Texture = <diffuseMap>;
    64. MinFilter = Linear;
    65. MagFilter = Linear;
    66. MipFilter = Linear;
    67. };
    68.  
    69. sampler2D normalMapSampler = sampler_state
    70. {
    71. Texture = <normalMap>;
    72. MinFilter = Linear;
    73. MagFilter = Linear;
    74. MipFilter = Linear;
    75. };
    76.  
    77. sampler2D specularMapSampler = sampler_state
    78. {
    79. Texture = <specularMap>;
    80. MinFilter = Linear;
    81. MagFilter = Linear;
    82. MipFilter = Linear;
    83. };
    84.  
    85. //////////////////////////////////////////////////////////////
    86. // Lights section
    87.  
    88. float4 lightPos : POSITION
    89. <
    90. string UIName = "Light Position";
    91. string Object = "PointLight";
    92. string Space = "World";
    93. int RefID = 0;
    94. > = {100.0f, 100.0f, 100.0f, 0.0f};
    95.  
    96. float4 lightColor : LIGHTCOLOR
    97. <
    98. int LightRef = 0;
    99. string UIWidget = "None";
    100. > = { 1.0f, 1.0f, 1.0f, 0.0f };
    101.  
    102. //////////////////////////////////////////////////////////////
    103. // Structs section
    104.  
    105. // input from application (Application 2 Vertex) These are "filled" from the app, you can get data from these
    106. struct a2v{
    107. float4 position : POSITION;
    108. float2 texCoord : TEXCOORD0;
    109. float4 normal : NORMAL; //float4 for multiplication purposes in 4x4 matrices
    110. float4 binormal : BINORMAL;
    111. float4 tangent : TANGENT;
    112. };
    113.  
    114. // output to fragment program (Vertex 2 Fragment) These are "filled" from the Vertex shader
    115. struct v2f{
    116. float4 position : POSITION;
    117. float2 texCoord : TEXCOORD0;
    118. float3 lightVec : TEXCOORD1;
    119. float3 worldNormal : TEXCOORD2;
    120. float3 worldBinormal: TEXCOORD3;
    121. float3 worldTangent : TEXCOORD4;
    122. float3 eyeVec : TEXCOORD5;
    123. };
    124.  
    125. //////////////////////////////////////////////////////////////
    126. // Vertex Shader
    127. v2f vShader(a2v In)
    128. {
    129. v2f Out;
    130. Out.worldNormal = mul(In.normal, WorldInverseTranspose).xyz;
    131. Out.worldTangent = mul(In.tangent, WorldInverseTranspose).xyz;
    132. Out.worldBinormal = mul(In.binormal, WorldInverseTranspose).xyz;
    133.  
    134. float3 worldSpacePos = mul(In.position, World).xyz; //transform vert pos to world space
    135. Out.eyeVec = ViewInverse[3] - worldSpacePos;
    136. Out.lightVec = lightPos - worldSpacePos;
    137. Out.texCoord = In.texCoord;
    138. Out.position = mul(In.position, wvp);
    139. return Out;
    140. }
    141.  
    142. //////////////////////////////////////////////////////////////
    143. // Pixel Shader
    144. float4 pShader(v2f In) : COLOR
    145. {
    146. //float4 outColor;
    147. float4 Out;
    148. float4 colorTexture = tex2D(diffuseMapSampler, In.texCoord);
    149. float3 normal = tex2D(normalMapSampler, In.texCoord).rgb * 2 - 1;
    150. float4 specTexture = tex2D(specularMapSampler, In.texCoord);
    151. float3x3 objToTangentSpace;
    152. objToTangentSpace[0] = normalize(In.worldBinormal);
    153. objToTangentSpace[1] = normalize(-In.worldTangent);
    154. objToTangentSpace[2] = normalize(In.worldNormal);
    155. if (bFlipGreenChannel) normal.g =-normal.g;
    156. float3 N = mul(normal, objToTangentSpace);
    157. N = normalize(N);
    158. float3 L = normalize(In.lightVec); //makes lightVec length of 1
    159. float3 V = normalize(In.eyeVec);
    160. float light = saturate(dot(N,L));
    161. float4 Diffuse = colorTexture * light * lightColor;
    162. float4 Ambient = ambientColor * colorTexture;
    163. float3 halfVec = normalize(V + L);
    164. float4 Specular = saturate(dot(halfVec, N)); //working out where spec should be
    165. float specPower = pow(glossiness, specTexture.a); //Power the Gloss map(specTexture alpha) by the glossiness spinner, so you can control gloss
    166. Specular = pow(Specular, specPower);//powering the spec by the powered gloss map
    167. Specular *= saturate(dot(N,L) * 4);//xoliul spec through surface bug fix
    168. Specular *= specTexture * lightColor;//multiplying by light color so spec dissappears with light.
    169. Out.rgb = Diffuse.rgb + Ambient.rgb + Specular.rgb;
    170. Out.a = 1.0f;
    171. return Out;
    172. }
    173.  
    174. //////////////////////////////////////////////////////////////
    175. // Techniques
    176. technique Simple
    177. {
    178. pass one
    179. {
    180. VertexShader = compile vs_3_0 vShader();
    181. PixelShader = compile ps_3_0 pShader();
    182. }
    183. }

    I think it looks stange, but I can't figure out what I am doing wrong.

    Another thing I have noticed is the further my omni is away the brighter and larger it becomes. Do I just ignore this for the time being until I go over light attenuation?

    Did manage to get a "Flip green channel" tick box in there which I was quite happy about though :)

    Edit: When I put Xoliul shader to gamma of 1, the light is very similar to mine, but still not as blown out. so may want to try and implement gamma into my shader. When set to 2.2 looks FAR better than mine.
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    Well, I just carried on plugging away and I have managed to get the spec working correctly, looks pretty much identical to XoliulShader (my benchmark to see if things are working correctly :p)

    I was multiplying and powering by the wrong things, I was trying to figure it out just form the Xoliul source, but it is a bit hard that way when you have to jump around the shader to see where it is getting values from.

    Here is my shader now in case anyone is interested.
    1. string ParamID = "0x003";
    2.  
    3. float4x4 wvp : WORLDVIEWPROJ < string UIWidget = "None"; >;
    4. float4x4 WorldInverseTranspose : WORLDINVERSETRANSPOSE < string UIWidget = "None"; >;
    5. float4x4 ViewInverse : VIEWINVERSE < string UIWidget = "None"; >;
    6. float4x4 World : WORLD < string UIWidget = "None"; >;
    7.  
    8. //////////////////////////////////////////////////////////////
    9. // Parameters section
    10.  
    11. float4 specularColor : SPECULAR
    12. <
    13. string UIName = "Specular Colour";
    14. //string UIWidget = "ColorSwatch";
    15. > = {0.15f, 0.15f, 0.15f, 1.0f};
    16.  
    17.  
    18. float4 ambientColor : AMBIENT
    19. <
    20. string UIName = "Ambient Colour";
    21. //string UIWidget = "ColorSwatch";
    22. > = {0.15f, 0.15f, 0.15f, 1.0f};
    23.  
    24. float glossiness
    25. <
    26. string UIName = "Glossiness Level";
    27. string UIType = "FloatSpinner";
    28. float UIMin = 0.0f;
    29. float UIMax = 100.0f;
    30. float UIStep = 0.05;
    31. > = 25.0f;
    32.  
    33.  
    34. texture diffuseMap : DiffuseMap
    35. <
    36. string name = "default_color.dds";
    37. string UIName = "Diffuse Texture";
    38. string TextureType = "2D";
    39. >;
    40.  
    41. texture normalMap : NormalMap
    42. <
    43. string name = "default_normal.dds";
    44. string UIName = "Normal Map";
    45. string TextureType = "2D";
    46. >;
    47.  
    48. bool bFlipGreenChannel
    49. <
    50. string gui = "slider";
    51. string UIName = "Flip Green";
    52. > = true;
    53.  
    54. texture specularMap : SpecularMap
    55. <
    56. string name = "default_color.dds";
    57. string UIName = "Specular Texture";
    58. string TextureType = "2D";
    59. >;
    60.  
    61. sampler2D diffuseMapSampler = sampler_state
    62. {
    63. Texture = <diffuseMap>;
    64. MinFilter = Linear;
    65. MagFilter = Linear;
    66. MipFilter = Linear;
    67. MipLODBias=-1;
    68. };
    69.  
    70. sampler2D normalMapSampler = sampler_state
    71. {
    72. Texture = <normalMap>;
    73. MinFilter = Linear;
    74. MagFilter = Linear;
    75. MipFilter = Linear;
    76. MipLODBias=-1;
    77. };
    78.  
    79. sampler2D specularMapSampler = sampler_state
    80. {
    81. Texture = <specularMap>;
    82. MinFilter = Linear;
    83. MagFilter = Linear;
    84. MipFilter = Linear;
    85. MipLODBias=-1;
    86. };
    87.  
    88. //////////////////////////////////////////////////////////////
    89. // Lights section
    90.  
    91. float4 lightPos : POSITION
    92. <
    93. string UIName = "Light Position";
    94. string Object = "PointLight";
    95. string Space = "World";
    96. int RefID = 0;
    97. > = {100.0f, 100.0f, 100.0f, 0.0f};
    98.  
    99. float4 lightColor : LIGHTCOLOR
    100. <
    101. int LightRef = 0;
    102. string UIWidget = "None";
    103. > = { 1.0f, 1.0f, 1.0f, 0.0f };
    104.  
    105. //////////////////////////////////////////////////////////////
    106. // Structs section
    107.  
    108. // input from application (Application 2 Vertex) These are "filled" from the app, you can get data from these
    109. struct a2v{
    110. float4 position : POSITION;
    111. float2 texCoord : TEXCOORD0;
    112. float4 normal : NORMAL; //float4 for multiplication purposes in 4x4 matrices
    113. float4 binormal : BINORMAL;
    114. float4 tangent : TANGENT;
    115. };
    116.  
    117. // output to fragment program (Vertex 2 Fragment) These are "filled" from the Vertex shader
    118. struct v2f{
    119. float4 position : POSITION;
    120. float2 texCoord : TEXCOORD0;
    121. float3 lightVec : TEXCOORD1;
    122. float3 worldNormal : TEXCOORD2;
    123. float3 worldBinormal: TEXCOORD3;
    124. float3 worldTangent : TEXCOORD4;
    125. float3 eyeVec : TEXCOORD5;
    126. };
    127.  
    128. //////////////////////////////////////////////////////////////
    129. // Vertex Shader
    130. v2f vShader(a2v In)
    131. {
    132. v2f Out;
    133. Out.worldNormal = mul(In.normal, WorldInverseTranspose).xyz;
    134. Out.worldTangent = mul(In.tangent, WorldInverseTranspose).xyz;
    135. Out.worldBinormal = mul(In.binormal, WorldInverseTranspose).xyz;
    136.  
    137. float3 worldSpacePos = mul(In.position, World).xyz; //transform vert pos to world space
    138. Out.eyeVec = ViewInverse[3] - worldSpacePos;
    139. Out.lightVec = lightPos - worldSpacePos;
    140. Out.texCoord = In.texCoord;
    141. Out.position = mul(In.position, wvp);
    142. return Out;
    143. }
    144.  
    145. //////////////////////////////////////////////////////////////
    146. // Pixel Shader
    147. float4 pShader(v2f In) : COLOR
    148. {
    149. //float4 outColor;
    150. float4 Out;
    151. float4 colorTexture = tex2D(diffuseMapSampler, In.texCoord);
    152. float3 normal = tex2D(normalMapSampler, In.texCoord).rgb * 2 - 1;
    153. float4 specTexture = tex2D(specularMapSampler, In.texCoord);
    154. float3x3 objToTangentSpace;
    155. objToTangentSpace[0] = normalize(In.worldBinormal);
    156. objToTangentSpace[1] = normalize(-In.worldTangent);
    157. objToTangentSpace[2] = normalize(In.worldNormal);
    158. if (bFlipGreenChannel) normal.g =-normal.g;
    159. float3 N = mul(normal, objToTangentSpace);
    160. N = normalize(N);
    161. float3 L = normalize(In.lightVec); //makes lightVec length of 1
    162. float3 V = normalize(In.eyeVec);
    163. float light = saturate(dot(N,L));
    164. float4 Diffuse = colorTexture * light * lightColor;
    165. float4 Ambient = ambientColor * colorTexture;
    166. float3 halfVec = normalize(V + L);
    167. float highlight = saturate(dot(halfVec, N)); //working out where spec should be
    168. float gloss = glossiness * specTexture.a; //Multiply gloss map (specmap.a) by the gloss spinner.
    169. float specPower = pow(highlight, gloss); //Power the highlight by the gloss texture
    170. float4 Specular = specPower * specTexture; //color the spec
    171. Specular *= saturate(dot(N,L) * 4);//xoliul spec through surface bug fix
    172. Specular *= lightColor;
    173. Out.rgb = Diffuse.rgb + Ambient.rgb + Specular.rgb;
    174. Out.a = 1.0f;
    175. return Out;
    176. }
    177.  
    178. //////////////////////////////////////////////////////////////
    179. // Techniques
    180. technique Simple
    181. {
    182. pass one
    183. {
    184. VertexShader = compile vs_3_0 vShader();
    185. PixelShader = compile ps_3_0 pShader();
    186. }
    187. }

    Another tech question: how would this work if it was being used in an actual game engine? Would the "glossiness spinner" be hard coded into the engine? or would it usually be something that is edited on a per model basis and carried into engine?

    Now I can carry on studying and will probably have another update next week with some cube map stuff :)
  • dnc
    Sorry I haven't posted in a while I'm on my last week of uni and am really busy.
    BUT
    There's some good stuff coming out of this guys, so keep it up!

    As for gloss maps I'm not sure what the standard (if there is one) method of doing the calculation for it is. Personally when I have used it (only once) I multiplied the gloss map with the specular power attribte. I don't know if this is right or not so I'm curious as to what other people do?
  • Xoliul
    Offline / Send Message
    Xoliul polycounter lvl 16
    (I'll kick this back up as it is one of the only interesting threads left around here)

    dnc: gloss map should be seen as a modifier for a rang of power values. if you just multiply it with a static power value, you cou8ld end up powering the specular to 0 (for pure black pixels on your gloss map). the best solution is to use the gloss map as a lerp alpha for your max and Min specular power values.
    Another tech question: how would this work if it was being used in an actual game engine? Would the "glossiness spinner" be hard coded into the engine? or would it usually be something that is edited on a per model basis and carried into engine?
    That really depends on the engine,but it would be a bit along these lines:
    Let's assume the engine does not do anything node-based. It can either figure out what variables are exposed by looking at the UI code in the FX file, or it could work with an extra XML file that sits next to the FX file, telling it what values do what (more flexible than just DXSAS). The engine could then expose whatever of these values it has collected when you want to edit this shader 'instance" (a case where a model gets drawn with a specific set of parameters for this shader). These instance parameters are then stored along with the asset, and applied when rendertime comes.
  • JackyBoy
    Offline / Send Message
    JackyBoy polycounter lvl 10
    haha, good to know I could make an interesting thread! :p

    That's actually really interesting to know how various engines can do it different ways. I love learning the process on how other engines do it, really helps me get my head around how to attack it from an art standpoint.

    I will definitely be carrying on my studies soon and reviving this thread, but unfortunately I have recently lost my job, so must prioritise getting work first. I will still be dipping in and out but can't focus as much time as I could before. Still love seeing how others are getting on though!
  • [Deleted User]
    Xoliul wrote: »
    I've never watched those, but Ben Cloward was the co-creator of ShaderFX, so that should say enough :)

    edit, some more stuff:

    The official Microsoft HLSL reference on MSDN. Quite technical and not always easy, but the best source to find clear answers on the syntax i think.
    Basic, old article I wrote on color math. It's not really up to standard anymore, but it should help a bit for the very basics. I promise, I want to rewrite and continue this stuff this year!

    And perhaps allow me to add my opinion on HLSL: I think it's the easiest, least nonsense and effort language there is. Once you feel at home in a shader's standard structure it's really very fun and satisfying to work with (just looking at that MSDN page makes me want to write stuff again haha). No setup required of program API's, no headaches with memory management, no compile steps, just CTRL-S, core functionality relatively portable between applications, etc...

    Do you know an equivalent for Maya?
  • illo
    grabbed this from another post
    http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf

    was wondering where I can get a refresher course in the math symbols, I recognize some of them, but I cant for the life of me remember what the other ones are. a full list would be appreciated if anyone has them, or an 'idiots guide to basic math sybols' for the wiki or something.(yeah, the wiki)
  • MikeF
    Offline / Send Message
    MikeF polycounter lvl 20
    Illo, this is my goto when working on stuff like this http://en.wikipedia.org/wiki/List_of_mathematical_symbols
2
Sign In or Register to comment.