1+ /* Copyright (c) 2025, Sascha Willems
2+ *
3+ * SPDX-License-Identifier: MIT
4+ *
5+ */
6+
7+ // Shader is looseley based on the ray tracing coding session by Inigo Quilez (www.iquilezles.org)
8+
9+ #define EPSILON 0 . 0001
10+ #define MAXLEN 1000 . 0
11+ #define SHADOW 0 . 5
12+ #define RAYBOUNCES 2
13+ #define REFLECTIONS true
14+ #define REFLECTIONSTRENGTH 0 . 4
15+ #define REFLECTIONFALLOFF 0 . 5
16+
17+ #define SceneObjectTypeSphere 0
18+ #define SceneObjectTypePlane 1
19+
20+ RWTexture2D < float4 > resultImage;
21+
22+ struct Camera
23+ {
24+ float3 pos;
25+ float3 lookat;
26+ float fov;
27+ };
28+
29+ struct UBO
30+ {
31+ float3 lightPos;
32+ float aspectRatio;
33+ float4 fogColor;
34+ Camera camera;
35+ float4x4 rotMat;
36+ };
37+ ConstantBuffer < UBO> ubo;
38+
39+ struct SceneObject
40+ {
41+ float4 objectProperties;
42+ float3 diffuse;
43+ float specular;
44+ int id;
45+ int objectType;
46+ };
47+ StructuredBuffer < SceneObject> sceneObjects;
48+
49+ void reflectRay(inout float3 rayD, in float3 mormal)
50+ {
51+ rayD = rayD + 2 . 0 * - dot(mormal, rayD) * mormal;
52+ }
53+
54+ // Lighting =========================================================
55+
56+ float lightDiffuse(float3 normal, float3 lightDir)
57+ {
58+ return clamp(dot(normal, lightDir), 0 . 1 , 1 . 0 );
59+ }
60+
61+ float lightSpecular(float3 normal, float3 lightDir, float specularFactor)
62+ {
63+ float3 viewVec = normalize(ubo .camera .pos );
64+ float3 halfVec = normalize(lightDir + viewVec);
65+ return pow(clamp(dot(normal, halfVec), 0 . 0 , 1 . 0 ), specularFactor);
66+ }
67+
68+ // Sphere ===========================================================
69+
70+ float sphereIntersect(in float3 rayO, in float3 rayD, in SceneObject sphere)
71+ {
72+ float3 oc = rayO - sphere .objectProperties .xyz ;
73+ float b = 2 . 0 * dot(oc, rayD);
74+ float c = dot(oc, oc) - sphere .objectProperties .w * sphere .objectProperties .w ;
75+ float h = b* b - 4 . 0 * c;
76+ if (h < 0 . 0 )
77+ {
78+ return - 1 . 0 ;
79+ }
80+ float t = (- b - sqrt(h)) / 2 . 0 ;
81+
82+ return t;
83+ }
84+
85+ float3 sphereNormal(in float3 pos, in SceneObject sphere)
86+ {
87+ return (pos - sphere .objectProperties .xyz ) / sphere .objectProperties .w ;
88+ }
89+
90+ // Plane ===========================================================
91+
92+ float planeIntersect(float3 rayO, float3 rayD, SceneObject plane)
93+ {
94+ float d = dot(rayD, plane .objectProperties .xyz );
95+
96+ if (d == 0 . 0 )
97+ return 0 . 0 ;
98+
99+ float t = - (plane .objectProperties .w + dot(rayO, plane .objectProperties .xyz )) / d;
100+
101+ if (t < 0 . 0 )
102+ return 0 . 0 ;
103+
104+ return t;
105+ }
106+
107+
108+ int intersect(in float3 rayO, in float3 rayD, inout float resT)
109+ {
110+ int id = - 1 ;
111+ float t = MAXLEN;
112+
113+ uint sceneObjectsLength;
114+ uint sceneObjectsStride;
115+ sceneObjects .GetDimensions (sceneObjectsLength, sceneObjectsStride);
116+
117+ for (int i = 0 ; i < sceneObjectsLength; i++ ) {
118+ // Sphere
119+ if (sceneObjects [i].objectType == SceneObjectTypeSphere) {
120+ t = sphereIntersect(rayO, rayD, sceneObjects [i]);
121+ }
122+ // Plane
123+ if (sceneObjects [i].objectType == SceneObjectTypePlane) {
124+ t = planeIntersect(rayO, rayD, sceneObjects [i]);
125+ }
126+ if ((t > EPSILON) && (t < resT))
127+ {
128+ id = sceneObjects [i].id ;
129+ resT = t;
130+ }
131+ }
132+
133+ return id;
134+ }
135+
136+ float calcShadow(in float3 rayO, in float3 rayD, in int objectId, inout float t)
137+ {
138+ uint sceneObjectsLength;
139+ uint sceneObjectsStride;
140+ sceneObjects .GetDimensions (sceneObjectsLength, sceneObjectsStride);
141+
142+ for (int i = 0 ; i < sceneObjectsLength; i++ ) {
143+ if (sceneObjects [i].id == objectId)
144+ continue ;
145+
146+ float tLoc = MAXLEN;
147+
148+ // Sphere
149+ if (sceneObjects [i].objectType == SceneObjectTypeSphere)
150+ {
151+ tLoc = sphereIntersect(rayO, rayD, sceneObjects [i]);
152+ }
153+ // Plane
154+ if (sceneObjects [i].objectType == SceneObjectTypePlane)
155+ {
156+ tLoc = planeIntersect(rayO, rayD, sceneObjects [i]);
157+ }
158+ if ((tLoc > EPSILON) && (tLoc < t))
159+ {
160+ t = tLoc;
161+ return SHADOW;
162+ }
163+ }
164+ return 1 . 0 ;
165+ }
166+
167+ float3 fog(in float t, in float3 color)
168+ {
169+ return lerp(color, ubo .fogColor .rgb , clamp(sqrt(t* t)/ 20 . 0 , 0 . 0 , 1 . 0 ));
170+ }
171+
172+ float3 renderScene(inout float3 rayO, inout float3 rayD, inout int id)
173+ {
174+ float3 color = float3(0 , 0 , 0 );
175+ float t = MAXLEN;
176+
177+ // Get intersected object ID
178+ int objectID = intersect(rayO, rayD, t);
179+
180+ if (objectID == - 1 )
181+ {
182+ return color;
183+ }
184+
185+ float3 pos = rayO + t * rayD;
186+ float3 lightVec = normalize(ubo .lightPos - pos);
187+ float3 normal;
188+
189+ uint sceneObjectsLength;
190+ uint sceneObjectsStride;
191+ sceneObjects .GetDimensions (sceneObjectsLength, sceneObjectsStride);
192+
193+ for (int i = 0 ; i < sceneObjectsLength; i++ ) {
194+ if (objectID == sceneObjects [i].id )
195+ {
196+ // Sphere
197+ if (sceneObjects [i].objectType == SceneObjectTypeSphere) {
198+ normal = sphereNormal(pos, sceneObjects [i]);
199+ }
200+ // Plane
201+ if (sceneObjects [i].objectType == SceneObjectTypePlane) {
202+ normal = sceneObjects [i].objectProperties .xyz ;
203+ }
204+ // Lighting
205+ float diffuse = lightDiffuse(normal, lightVec);
206+ float specular = lightSpecular(normal, lightVec, sceneObjects [i].specular );
207+ color = diffuse * sceneObjects [i].diffuse + specular;
208+ }
209+ }
210+
211+ if (id == - 1 )
212+ return color;
213+
214+ id = objectID;
215+
216+ // Shadows
217+ t = length(ubo .lightPos - pos);
218+ color *= calcShadow(pos, lightVec, id, t);
219+
220+ // Fog
221+ color = fog(t, color);
222+
223+ // Reflect ray for next render pass
224+ reflectRay(rayD, normal);
225+ rayO = pos;
226+
227+ return color;
228+ }
229+
230+ [shader(" compute" )]
231+ [numthreads(16 , 16 , 1 )]
232+ void computeMain(uint3 GlobalInvocationID : SV_DispatchThreadID)
233+ {
234+ int2 dim;
235+ resultImage .GetDimensions (dim .x , dim .y );
236+ float2 uv = float2(GlobalInvocationID .xy ) / dim;
237+
238+ float3 rayO = ubo .camera .pos ;
239+ float3 rayD = normalize(float3((- 1 . 0 + 2 . 0 * uv) * float2(ubo .aspectRatio , 1 . 0 ), - 1 . 0 ));
240+
241+ // Basic color path
242+ int id = 0 ;
243+ float3 finalColor = renderScene(rayO, rayD, id);
244+
245+ // Reflection
246+ if (REFLECTIONS)
247+ {
248+ float reflectionStrength = REFLECTIONSTRENGTH;
249+ for (int i = 0 ; i < RAYBOUNCES; i++ )
250+ {
251+ float3 reflectionColor = renderScene(rayO, rayD, id);
252+ finalColor = (1 . 0 - reflectionStrength) * finalColor + reflectionStrength * lerp(reflectionColor, finalColor, 1 . 0 - reflectionStrength);
253+ reflectionStrength *= REFLECTIONFALLOFF;
254+ }
255+ }
256+
257+ resultImage [int2(GlobalInvocationID .xy )] = float4(finalColor, 0 . 0 );
258+ }
0 commit comments