1+ /* Copyright (c) 2025, Sascha Willems
2+ *
3+ * SPDX-License-Identifier: MIT
4+ *
5+ */
6+
7+ struct VSInput
8+ {
9+ float3 Pos;
10+ float2 UV;
11+ float3 Normal;
12+ float4 Tangent;
13+ };
14+
15+ struct VSOutput
16+ {
17+ float4 Pos : SV_POSITION;
18+ float2 UV;
19+ float3 LightVec;
20+ float3 LightVecB;
21+ float3 LightDir;
22+ float3 ViewVec;
23+ };
24+
25+ struct UBO
26+ {
27+ float4x4 projection;
28+ float4x4 model;
29+ float4x4 normal;
30+ float4 lightPos;
31+ };
32+ ConstantBuffer < UBO> ubo;
33+
34+ Sampler2D samplerColorMap;
35+ Sampler2D samplerNormalHeightMap;
36+
37+ #define lightRadius 45 . 0
38+
39+ [shader(" vertex" )]
40+ VSOutput vertexMain(VSInput input)
41+ {
42+ VSOutput output;
43+ float3 vertexPosition = mul(ubo .model , float4(input .Pos , 1 . 0 )).xyz ;
44+ output .LightDir = normalize(ubo .lightPos .xyz - vertexPosition);
45+
46+ float3 biTangent = cross(input .Normal , input .Tangent .xyz );
47+
48+ // Setup (t)angent-(b)inormal-(n)ormal matrix for converting
49+ // object coordinates into tangent space
50+ float3x3 tbnMatrix;
51+ tbnMatrix [0 ] = mul((float3x3 )ubo .normal , input .Tangent .xyz );
52+ tbnMatrix [1 ] = mul((float3x3 )ubo .normal , biTangent);
53+ tbnMatrix [2 ] = mul((float3x3 )ubo .normal , input .Normal );
54+
55+ output .LightVec .xyz = mul(float3(ubo .lightPos .xyz - vertexPosition), tbnMatrix);
56+
57+ float3 lightDist = ubo .lightPos .xyz - input .Pos ;
58+ output .LightVecB .x = dot(input .Tangent .xyz , lightDist);
59+ output .LightVecB .y = dot(biTangent, lightDist);
60+ output .LightVecB .z = dot(input .Normal , lightDist);
61+
62+ output .ViewVec .x = dot(input .Tangent .xyz , input .Pos );
63+ output .ViewVec .y = dot(biTangent, input .Pos );
64+ output .ViewVec .z = dot(input .Normal , input .Pos );
65+
66+ output .UV = input .UV ;
67+
68+ output .Pos = mul(ubo .projection , mul(ubo .model , float4(input .Pos , 1 . 0 )));
69+ return output;
70+ }
71+
72+ [shader(" fragment" )]
73+ float4 fragmentMain(VSOutput input)
74+ {
75+ float3 specularColor = float3(0 . 85 , 0 . 5 , 0 . 0 );
76+
77+ float invRadius = 1 . 0 / lightRadius;
78+ float ambient = 0 . 25 ;
79+
80+ float3 rgb, normal;
81+
82+ rgb = samplerColorMap .Sample (input .UV ).rgb ;
83+ normal = normalize((samplerNormalHeightMap .Sample (input .UV ).rgb - 0 . 5 ) * 2 . 0 );
84+
85+ float distSqr = dot(input .LightVecB , input .LightVecB );
86+ float3 lVec = input .LightVecB * rsqrt(distSqr);
87+
88+ float atten = max(clamp(1 . 0 - invRadius * sqrt(distSqr), 0 . 0 , 1 . 0 ), ambient);
89+ float diffuse = clamp(dot(lVec, normal), 0 . 0 , 1 . 0 );
90+
91+ float3 light = normalize(- input .LightVec );
92+ float3 view = normalize(input .ViewVec );
93+ float3 reflectDir = reflect(- light, normal);
94+
95+ float specular = pow(max(dot(view, reflectDir), 0 . 0 ), 4 . 0 );
96+
97+ return float4((rgb * atten + (diffuse * rgb + 0 . 5 * specular * specularColor .rgb )) * atten, 1 . 0 );
98+ }
0 commit comments