TrianglePixelShader.hlsl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Shader that uses a shadow map for rendering shadows, see:
  2. // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/
  3. // https://takinginitiative.wordpress.com/2011/05/25/directx10-tutorial-10-shadow-mapping-part-2/
  4. Texture2D LightDepthTexture : register(t2);
  5. SamplerComparisonState LightDepthSampler : register(s2);
  6. cbuffer PixelShaderConstantBuffer : register(b1)
  7. {
  8. float3 CameraPos;
  9. float3 LightPos;
  10. };
  11. struct PS_INPUT
  12. {
  13. float4 Position : SV_POSITION; // interpolated vertex position
  14. float3 Normal : TEXCOORD0;
  15. float3 WorldPos : TEXCOORD1;
  16. float2 Tex : TEXCOORD2;
  17. float4 PositionL : TEXCOORD3; // interpolated vertex position in light space
  18. float4 Color : COLOR0;
  19. };
  20. struct PS_OUTPUT
  21. {
  22. float4 RGBColor : SV_TARGET;
  23. };
  24. PS_OUTPUT main(PS_INPUT input)
  25. {
  26. // Constants
  27. float AmbientFactor = 0.3;
  28. float3 DiffuseColor = float3(input.Color.r, input.Color.g, input.Color.b);
  29. float3 SpecularColor = float3(1, 1, 1);
  30. float SpecularPower = 100.0;
  31. float bias = 1.0e-7;
  32. // Homogenize position in light space
  33. input.PositionL.xyz /= input.PositionL.w;
  34. // Calculate dot product between direction to light and surface normal and clamp between [0, 1]
  35. float3 view_dir = normalize(CameraPos - input.WorldPos);
  36. float3 world_to_light = LightPos - input.WorldPos;
  37. float3 light_dir = normalize(world_to_light);
  38. float3 normal = normalize(input.Normal);
  39. if (dot(view_dir, normal) < 0) // If we're viewing the triangle from the back side, flip the normal to get the correct lighting
  40. normal = -normal;
  41. float normal_dot_light_dir = saturate(dot(normal, light_dir));
  42. // Calculate texture coordinates in light depth texture
  43. float2 tex_coord;
  44. tex_coord.x = input.PositionL.x / 2.0 + 0.5;
  45. tex_coord.y = -input.PositionL.y / 2.0 + 0.5;
  46. // Check that the texture coordinate is inside the depth texture, if not we don't know if it is lit or not so we assume lit
  47. float shadow_factor = 1.0;
  48. if (input.Color.a > 0 // Alpha = 0 means don't receive shadows
  49. && tex_coord.x == saturate(tex_coord.x) && tex_coord.y == saturate(tex_coord.y))
  50. {
  51. // Modify shadow bias according to the angle between the normal and the light dir
  52. float modified_bias = bias * tan(acos(normal_dot_light_dir));
  53. modified_bias = min(modified_bias, 10.0 * bias);
  54. // Get texture size
  55. float width, height, levels;
  56. LightDepthTexture.GetDimensions(0, width, height, levels);
  57. width = 1.0 / width;
  58. height = 1.0 / height;
  59. // Samples to take
  60. uint num_samples = 16;
  61. float2 offsets[] = {
  62. float2(-1.5 * width, -1.5 * height),
  63. float2(-0.5 * width, -1.5 * height),
  64. float2(0.5 * width, -1.5 * height),
  65. float2(1.5 * width, -1.5 * height),
  66. float2(-1.5 * width, -0.5 * height),
  67. float2(-0.5 * width, -0.5 * height),
  68. float2(0.5 * width, -0.5 * height),
  69. float2(1.5 * width, -0.5 * height),
  70. float2(-1.5 * width, 0.5 * height),
  71. float2(-0.5 * width, 0.5 * height),
  72. float2(0.5 * width, 0.5 * height),
  73. float2(1.5 * width, 0.5 * height),
  74. float2(-1.5 * width, 1.5 * height),
  75. float2(-0.5 * width, 1.5 * height),
  76. float2(0.5 * width, 1.5 * height),
  77. float2(1.5 * width, 1.5 * height),
  78. };
  79. // Calculate depth of this pixel relative to the light
  80. float light_depth = input.PositionL.z + modified_bias;
  81. // Sample shadow factor
  82. shadow_factor = 0.0;
  83. [unroll] for (uint i = 0; i < num_samples; ++i)
  84. shadow_factor += LightDepthTexture.SampleCmp(LightDepthSampler, tex_coord + offsets[i], light_depth);
  85. shadow_factor /= num_samples;
  86. }
  87. // Calculate diffuse and specular
  88. float diffuse = normal_dot_light_dir;
  89. float specular = diffuse > 0.0? pow(saturate(-dot(reflect(light_dir, normal), view_dir)), SpecularPower) : 0.0;
  90. // Apply procedural pattern based on the uv coordinates
  91. bool2 less_half = input.Tex - floor(input.Tex) < float2(0.5, 0.5);
  92. float darken_factor = less_half.r ^ less_half.g? 0.5 : 1.0;
  93. // Fade out checkerboard pattern when it tiles too often
  94. float2 dx = ddx(input.Tex), dy = ddy(input.Tex);
  95. float texel_distance = sqrt(dot(dx, dx) + dot(dy, dy));
  96. darken_factor = lerp(darken_factor, 0.75, clamp(5.0 * texel_distance - 1.5, 0.0, 1.0));
  97. // Calculate color
  98. PS_OUTPUT output;
  99. output.RGBColor = float4(saturate((AmbientFactor + diffuse * shadow_factor) * darken_factor * DiffuseColor + SpecularColor * specular * shadow_factor), 1);
  100. return output;
  101. }