Lynix

NZSL 1.1 PBR shader

Oct 2nd, 2025
880
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 12.26 KB | None | 0 0
  1. [nzsl_version("1.1")]
  2. module PhysicallyBasedMaterial;
  3.  
  4. import InstanceData from Engine.InstanceData;
  5. import LightData from Engine.LightData;
  6. import SkeletalData from Engine.SkeletalData;
  7. import ViewerData from Engine.ViewerData;
  8.  
  9. import * from Engine.LightShadow;
  10. import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear;
  11.  
  12. // Pass-specific options
  13. option DepthPass: bool = false;
  14. option DistanceDepth: bool = false;
  15. option ShadowPass: bool = false;
  16.  
  17. // Basic material options
  18. option HasBaseColorTexture: bool = false;
  19. option HasAlphaTexture: bool = false;
  20. option AlphaTest: bool = false;
  21.  
  22. // Physically-based material options
  23. option HasEmissiveTexture: bool = false;
  24. option HasHeightTexture: bool = false;
  25. option HasMetallicTexture: bool = false;
  26. option HasNormalTexture: bool = false;
  27. option HasRoughnessTexture: bool = false;
  28. option HasSpecularTexture: bool = false;
  29.  
  30. // Billboard related options
  31. option Billboard: bool = false;
  32.  
  33. // Vertex declaration related options
  34. const InvalidLoc = u32.Max;
  35.  
  36. option VertexColorLoc: u32 = InvalidLoc;
  37. option VertexNormalLoc: u32 = InvalidLoc;
  38. option VertexPositionLoc: u32 = InvalidLoc;
  39. option VertexSizeRotLocation: u32 = InvalidLoc;
  40. option VertexTangentLoc: u32 = InvalidLoc;
  41. option VertexUvLoc: u32 = InvalidLoc;
  42.  
  43. option VertexJointIndicesLoc: u32 = InvalidLoc;
  44. option VertexJointWeightsLoc: u32 = InvalidLoc;
  45.  
  46. option MaxLightCount: u32 = 3;
  47.  
  48. const HasNormal = (VertexNormalLoc != InvalidLoc);
  49. const HasVertexColor = (VertexColorLoc != InvalidLoc);
  50. const HasColor = (HasVertexColor || Billboard);
  51. const HasTangent = (VertexTangentLoc != InvalidLoc);
  52. const HasVertexUV = (VertexUvLoc != InvalidLoc);
  53. const HasUV = (HasVertexUV);
  54. const HasNormalMapping = HasNormalTexture && HasNormal && HasTangent && !DepthPass;
  55. const HasSkinning = (VertexJointIndicesLoc != InvalidLoc && VertexJointWeightsLoc != InvalidLoc);
  56.  
  57. [layout(std140)]
  58. struct MaterialSettings
  59. {
  60.     // Basic settings
  61.     [tag("AlphaTestThreshold")]
  62.     AlphaThreshold: f32,
  63.  
  64.     [tag("ShadowMapNormalOffset")]
  65.     ShadowMapNormalOffset: f32,
  66.  
  67.     [tag("ShadowPosScale")]
  68.     ShadowPosScale: f32,
  69.  
  70.     [tag("BaseColor")]
  71.     BaseColor: vec4[f32],
  72.  
  73.     // PBR settings
  74.     [tag("MetallicFactor")]
  75.     MetallicFactor: f32,
  76.  
  77.     [tag("RoughnessFactor")]
  78.     RoughnessFactor: f32
  79. }
  80.  
  81. // TODO: Add enums
  82. const DirectionalLight = 0;
  83. const PointLight = 1;
  84. const SpotLight = 2;
  85.  
  86. [tag("Material")]
  87. [auto_binding]
  88. external
  89. {
  90.     [tag("Settings")] settings: uniform[MaterialSettings],
  91.     [tag("BaseColorMap")] MaterialBaseColorMap: sampler2D[f32],
  92.     [tag("AlphaMap")] MaterialAlphaMap: sampler2D[f32],
  93.     [tag("EmissiveMap")] MaterialEmissiveMap: sampler2D[f32],
  94.     [tag("HeightMap")] MaterialHeightMap: sampler2D[f32],
  95.     [tag("MetallicMap")] MaterialMetallicMap: sampler2D[f32],
  96.     [tag("NormalMap")] MaterialNormalMap: sampler2D[f32],
  97.     [tag("RoughnessMap")] MaterialRoughnessMap: sampler2D[f32],
  98.     [tag("SpecularMap")] MaterialSpecularMap: sampler2D[f32],
  99. }
  100.  
  101. [tag("Engine")]
  102. [auto_binding]
  103. external
  104. {
  105.     [tag("TextureOverlay")] TextureOverlay: sampler2D[f32],
  106.     [tag("InstanceData")] instanceData: uniform[InstanceData],
  107.     [tag("ViewerData")] viewerData: uniform[ViewerData],
  108.     [tag("SkeletalData")] skeletalData: uniform[SkeletalData],
  109.     [tag("LightData")] lightData: uniform[LightData],
  110.     [tag("ShadowMapsDirectional")] shadowMapsDirectional: array[depth_sampler2D_array[f32], MaxLightCount],
  111.     [tag("ShadowMapsPoint")] shadowMapsPoint: array[sampler_cube[f32], MaxLightCount],
  112.     [tag("ShadowMapsSpot")] shadowMapsSpot: array[depth_sampler2D[f32], MaxLightCount],
  113. }
  114.  
  115. [export]
  116. struct VertOut
  117. {
  118.     [location(0)] worldPos: vec3[f32],
  119.     [location(1), cond(HasUV)] uv: vec2[f32],
  120.     [location(2), cond(HasColor)] color: vec4[f32],
  121.     [location(3), cond(HasNormal)] normal: vec3[f32],
  122.     [location(4), cond(HasNormalMapping)] tangent: vec3[f32],
  123.     [builtin(position)] position: vec4[f32],
  124. }
  125.  
  126. // Fragment stage
  127. import ComputeLightRadiance, DistributionGGX, GeometrySmith, FresnelSchlick from Math.CookTorrancePBR;
  128. import Pi from Math.Constants;
  129.  
  130. struct FragOut
  131. {
  132.     [location(0)] RenderTarget0: vec4[f32],
  133.     [builtin(frag_depth), cond(DistanceDepth)] fragdepth: f32,
  134. }
  135.  
  136. [export]
  137. fn ComputeColor(input: VertOut) -> vec4[f32]
  138. {
  139.     let color = settings.BaseColor;
  140.  
  141.     const if (HasUV)
  142.         color.a *= TextureOverlay.Sample(input.uv).r;
  143.  
  144.     const if (HasColor)
  145.         color *= input.color;
  146.  
  147.     const if (HasBaseColorTexture)
  148.         color *= MaterialBaseColorMap.Sample(input.uv);
  149.  
  150.     const if (HasAlphaTexture)
  151.         color.w *= MaterialAlphaMap.Sample(input.uv).x;
  152.  
  153.     const if (AlphaTest)
  154.     {
  155.         if (color.w < settings.AlphaThreshold)
  156.             discard;
  157.     }
  158.  
  159.     return color;
  160. }
  161.  
  162. [export, cond(HasNormal)]
  163. fn ComputeLighting(color: vec3[f32], input: VertOut) -> vec3[f32]
  164. {
  165.     let lightRadiance = vec3[f32](0.0, 0.0, 0.0);
  166.  
  167.     let eyeVec = normalize(viewerData.eyePosition - input.worldPos);
  168.  
  169.     let normal: vec3[f32];
  170.     const if (HasNormalMapping)
  171.     {
  172.         let N = normalize(input.normal);
  173.         let T = normalize(input.tangent);
  174.         let B = cross(N, T);
  175.         let tbnMatrix = mat3[f32](T, B, N);
  176.  
  177.         normal = normalize(tbnMatrix * (MaterialNormalMap.Sample(input.uv).xyz * 2.0 - vec3[f32](1.0, 1.0, 1.0)));
  178.     }
  179.     else
  180.         normal = normalize(input.normal);
  181.  
  182.     let albedo = color.xyz;
  183.  
  184.     let metallic = settings.MetallicFactor;
  185.     const if (HasMetallicTexture)
  186.         metallic *= MaterialMetallicMap.Sample(input.uv).x;
  187.  
  188.     let roughness = settings.RoughnessFactor;
  189.     const if (HasRoughnessTexture)
  190.         roughness *= MaterialRoughnessMap.Sample(input.uv).x;
  191.  
  192.     let F0 = vec3[f32](0.04, 0.04, 0.04);
  193.     F0 = lerp(F0, albedo, metallic.rrr);
  194.  
  195.     let albedoFactor = albedo / Pi;
  196.  
  197.     for lightIndex in u32(0) -> lightData.directionalLightCount
  198.     {
  199.         let light = lightData.directionalLights[lightIndex];
  200.  
  201.         let lambert = max(dot(normal, -light.direction), 0.0);
  202.  
  203.         let shadowFactor = ComputeDirectionalLightShadow(light, shadowMapsDirectional[lightIndex], input.worldPos, lambert, viewerData.viewMatrix);
  204.         let radiance = ComputeLightRadiance(light.color.rgb, -light.direction, albedoFactor, eyeVec, F0, normal, metallic, roughness);
  205.  
  206.         lightRadiance += shadowFactor * radiance;
  207.     }
  208.  
  209.     for lightIndex in u32(0) -> lightData.pointLightCount
  210.     {
  211.         let light = lightData.pointLights[lightIndex];
  212.  
  213.         let lightToPos = input.worldPos - light.position;
  214.         let dist = length(lightToPos);
  215.  
  216.         let attenuation = max(1.0 - dist * light.invRadius, 0.0);
  217.         let lightToPosNorm = lightToPos / max(dist, 0.0001);
  218.  
  219.         let shadowFactor = ComputePointLightShadow(light, shadowMapsPoint[lightIndex], dist, lightToPosNorm);
  220.         let radiance = ComputeLightRadiance(light.color.rgb * attenuation, -lightToPosNorm, albedoFactor, eyeVec, F0, normal, metallic, roughness);
  221.  
  222.         lightRadiance += shadowFactor * radiance;
  223.     }
  224.  
  225.     for lightIndex in u32(0) -> lightData.spotLightCount
  226.     {
  227.         let light = lightData.spotLights[lightIndex];
  228.  
  229.         let lightToPos = input.worldPos - light.position;
  230.         let dist = length(lightToPos);
  231.         let lightToPosNorm = lightToPos / max(dist, 0.0001);
  232.  
  233.         let curAngle = dot(light.direction, lightToPosNorm);
  234.         let innerMinusOuterAngle = light.innerAngle - light.outerAngle;
  235.  
  236.         let attenuation = max(1.0 - dist * light.invRadius, 0.0);
  237.         attenuation *= max((curAngle - light.outerAngle) / innerMinusOuterAngle, 0.0);         
  238.  
  239.         let lambert = clamp(dot(normal, -lightToPosNorm), 0.0, 1.0);
  240.  
  241.         let shadowFactor = ComputeSpotLightShadow(light, shadowMapsSpot[lightIndex], input.worldPos, lambert);
  242.         let radiance = ComputeLightRadiance(light.color.rgb * attenuation, -lightToPosNorm, albedoFactor, eyeVec, F0, normal, metallic, roughness);
  243.  
  244.         lightRadiance += shadowFactor * radiance;
  245.     }
  246.  
  247.     let ambient = (0.03).rrr * albedo;
  248.  
  249.     let finalColor = ambient + lightRadiance;
  250.     finalColor = finalColor / (finalColor + vec3[f32](1.0, 1.0, 1.0));
  251.  
  252.     return finalColor;
  253. }
  254.  
  255. [export, entry(frag), cond(!DepthPass)]
  256. fn FragMain(input: VertOut) -> FragOut
  257. {
  258.     let color = ComputeColor(input);
  259.  
  260.     const if (HasNormal)
  261.         color.rgb = ComputeLighting(color.rgb, input);
  262.  
  263.     let output: FragOut;
  264.     output.RenderTarget0 = color;
  265.     return output;
  266. }
  267.  
  268. // Shadow passes entries
  269. [entry(frag), cond(DepthPass && DistanceDepth)]
  270. [depth_write(replace)]
  271. fn FragDepthDist(input: VertOut) -> FragOut
  272. {
  273.     let color = ComputeColor(input);
  274.  
  275.     let output: FragOut;
  276.     output.RenderTarget0 = color;
  277.  
  278.     let dist = distance(viewerData.eyePosition, input.worldPos);
  279.     output.fragdepth = dist / viewerData.farPlane;
  280.  
  281.     return output;
  282. }
  283.  
  284. [entry(frag), cond(DepthPass && AlphaTest && !DistanceDepth)]
  285. fn FragDepth(input: VertOut) -> FragOut
  286. {
  287.     let color = ComputeColor(input);
  288.  
  289.     let output: FragOut;
  290.     output.RenderTarget0 = color;
  291.     return output;
  292. }
  293.  
  294. [entry(frag), cond(DepthPass && !AlphaTest && !DistanceDepth)]
  295. fn FragDepthNoAlpha() {} //< dummy
  296.  
  297. // Vertex stage
  298. [cond(!Billboard)]
  299. struct VertIn
  300. {
  301.     [location(VertexPositionLoc)]
  302.     pos: vec3[f32],
  303.  
  304.     [cond(HasVertexColor), location(VertexColorLoc)]
  305.     color: vec4[f32],
  306.  
  307.     [cond(HasVertexUV), location(VertexUvLoc)]
  308.     uv: vec2[f32],
  309.  
  310.     [cond(HasNormal), location(VertexNormalLoc)]
  311.     normal: vec3[f32],
  312.  
  313.     [cond(HasTangent), location(VertexTangentLoc)]
  314.     tangent: vec3[f32],
  315.  
  316.     [cond(HasSkinning), location(VertexJointIndicesLoc)]
  317.     jointIndices: vec4[i32],
  318.  
  319.     [cond(HasSkinning), location(VertexJointWeightsLoc)]
  320.     jointWeights: vec4[f32]
  321. }
  322.  
  323. [cond(Billboard)]
  324. struct BillboardVertIn
  325. {
  326.     [builtin(vertex_index)] vertIndex: i32,
  327.  
  328.     [location(VertexSizeRotLocation)]
  329.     sizeRot: vec4[f32], //< width,height,sin,cos
  330.  
  331.     [cond(HasVertexUV), location(VertexUvLoc)]
  332.     uv: vec2[f32],
  333.  
  334.     [cond(HasVertexColor), location(VertexColorLoc)]
  335.     color: vec4[f32]
  336. }
  337.  
  338. const billboardPos = array[vec2[f32]](
  339.     vec2[f32](-0.5, -0.5),
  340.     vec2[f32]( 0.5, -0.5),
  341.     vec2[f32](-0.5, 0.5),
  342.     vec2[f32]( 0.5, 0.5)
  343. );
  344.  
  345. [entry(vert), cond(Billboard)]
  346. fn VertBillboard(input: BillboardVertIn) -> VertOut
  347. {
  348.     let position = billboardPos[input.vertIndex % 4];
  349.  
  350.     let size = input.sizeRot.xy;
  351.     let sinCos = input.sizeRot.zw;
  352.  
  353.     let rotatedPosition = vec2[f32](
  354.         position.x * sinCos.y - position.y * sinCos.x,
  355.         position.y * sinCos.y + position.x * sinCos.x
  356.     );
  357.     rotatedPosition *= size;
  358.  
  359.     let cameraRight = vec3[f32](viewerData.viewMatrix[0][0], viewerData.viewMatrix[1][0], viewerData.viewMatrix[2][0]);
  360.     let cameraUp = vec3[f32](viewerData.viewMatrix[0][1], viewerData.viewMatrix[1][1], viewerData.viewMatrix[2][1]);
  361.  
  362.     let worldPosition = vec3[f32](instanceData.worldMatrix[3].xyz);
  363.     worldPosition += cameraRight * rotatedPosition.x;
  364.     worldPosition += cameraUp * rotatedPosition.y;
  365.  
  366.     let output: VertOut;
  367.     output.worldPos = worldPosition;
  368.     output.position = viewerData.viewProjMatrix * vec4[f32](worldPosition, 1.0);
  369.  
  370.     const if (HasVertexUV)
  371.         output.uv = input.uv;
  372.  
  373.     const if (HasColor)
  374.         output.color = input.color;
  375.  
  376.     return output;
  377. }
  378.  
  379. [entry(vert), cond(!Billboard)]
  380. fn VertMain(input: VertIn) -> VertOut
  381. {
  382.     let pos: vec3[f32];
  383.     const if (HasNormal) let normal: vec3[f32];
  384.  
  385.     const if (HasSkinning)
  386.     {
  387.         let jointMatrices = array[mat4[f32]](
  388.             skeletalData.jointMatrices[input.jointIndices[0]],
  389.             skeletalData.jointMatrices[input.jointIndices[1]],
  390.             skeletalData.jointMatrices[input.jointIndices[2]],
  391.             skeletalData.jointMatrices[input.jointIndices[3]]
  392.         );
  393.  
  394.         const if (HasNormal)
  395.         {
  396.             let skinningOutput = SkinLinearPositionNormal(jointMatrices, input.jointWeights, input.pos, input.normal);
  397.             pos = skinningOutput.position;
  398.             normal = skinningOutput.normal;
  399.         }
  400.         else
  401.         {
  402.             let skinningOutput = SkinLinearPosition(jointMatrices, input.jointWeights, input.pos);
  403.             pos = skinningOutput.position;
  404.         }
  405.     }
  406.     else
  407.     {
  408.         pos = input.pos;
  409.         const if (HasNormal)
  410.             normal = input.normal;
  411.     }
  412.  
  413.     const if (ShadowPass)
  414.     {
  415.         pos *= settings.ShadowPosScale;
  416.         const if (HasNormal)
  417.             pos -= normal * settings.ShadowMapNormalOffset;
  418.     }
  419.  
  420.     let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0);
  421.  
  422.     let output: VertOut;
  423.     output.worldPos = worldPosition.xyz;
  424.     output.position = viewerData.viewProjMatrix * worldPosition;
  425.  
  426.     let rotationMatrix = transpose(inverse(mat3[f32](instanceData.worldMatrix)));
  427.  
  428.     const if (HasColor)
  429.         output.color = input.color;
  430.  
  431.     const if (HasNormal)
  432.         output.normal = rotationMatrix * input.normal;
  433.  
  434.     const if (HasVertexUV)
  435.         output.uv = input.uv;
  436.  
  437.     const if (HasNormalMapping)
  438.         output.tangent = rotationMatrix * input.tangent;
  439.  
  440.     return output;
  441. }
Tags: Shader nzsl
Advertisement
Add Comment
Please, Sign In to add comment