├── code ├── apply_primary_shading.hlsl ├── build.bat ├── calc_variance.hlsl ├── ch_math.h ├── clear.hlsl ├── common.h ├── compute_disocclusion.hlsl ├── constants.hlsl ├── coolbox.hlsl ├── cornellbox.hlsl ├── correlate_history.hlsl ├── d3d12_utils.h ├── demo.hlsl ├── edge_avoiding_functions.hlsl ├── interior.hlsl ├── main.cpp ├── math.hlsl ├── note ├── pack_gbuffer.hlsl ├── pathtrace.hlsl ├── primary.hlsl ├── quaternion.hlsl ├── random.hlsl ├── rasterize_text.hlsl ├── raymarch.hlsl ├── scene.hlsl ├── sdf_engine.cpp ├── sdf_engine.h ├── spatial_filter.hlsl ├── sphere2.hlsl ├── stb_image.h ├── stb_image_write.h ├── stb_truetype.h ├── taa.hlsl ├── temporal_filter.hlsl ├── todo.txt ├── tonemap.hlsl ├── tron.hlsl ├── tunnel.hlsl ├── ui.cpp └── ui.h ├── data ├── liberation-mono.ttf └── textures │ ├── HDR_RGBA_0.png │ ├── HDR_RGBA_1.png │ ├── HDR_RGBA_10.png │ ├── HDR_RGBA_11.png │ ├── HDR_RGBA_12.png │ ├── HDR_RGBA_13.png │ ├── HDR_RGBA_14.png │ ├── HDR_RGBA_15.png │ ├── HDR_RGBA_16.png │ ├── HDR_RGBA_17.png │ ├── HDR_RGBA_18.png │ ├── HDR_RGBA_19.png │ ├── HDR_RGBA_2.png │ ├── HDR_RGBA_20.png │ ├── HDR_RGBA_21.png │ ├── HDR_RGBA_22.png │ ├── HDR_RGBA_23.png │ ├── HDR_RGBA_24.png │ ├── HDR_RGBA_25.png │ ├── HDR_RGBA_26.png │ ├── HDR_RGBA_27.png │ ├── HDR_RGBA_28.png │ ├── HDR_RGBA_29.png │ ├── HDR_RGBA_3.png │ ├── HDR_RGBA_30.png │ ├── HDR_RGBA_31.png │ ├── HDR_RGBA_32.png │ ├── HDR_RGBA_33.png │ ├── HDR_RGBA_34.png │ ├── HDR_RGBA_35.png │ ├── HDR_RGBA_36.png │ ├── HDR_RGBA_37.png │ ├── HDR_RGBA_38.png │ ├── HDR_RGBA_39.png │ ├── HDR_RGBA_4.png │ ├── HDR_RGBA_40.png │ ├── HDR_RGBA_41.png │ ├── HDR_RGBA_42.png │ ├── HDR_RGBA_43.png │ ├── HDR_RGBA_44.png │ ├── HDR_RGBA_45.png │ ├── HDR_RGBA_46.png │ ├── HDR_RGBA_47.png │ ├── HDR_RGBA_48.png │ ├── HDR_RGBA_49.png │ ├── HDR_RGBA_5.png │ ├── HDR_RGBA_50.png │ ├── HDR_RGBA_51.png │ ├── HDR_RGBA_52.png │ ├── HDR_RGBA_53.png │ ├── HDR_RGBA_54.png │ ├── HDR_RGBA_55.png │ ├── HDR_RGBA_56.png │ ├── HDR_RGBA_57.png │ ├── HDR_RGBA_58.png │ ├── HDR_RGBA_59.png │ ├── HDR_RGBA_6.png │ ├── HDR_RGBA_60.png │ ├── HDR_RGBA_61.png │ ├── HDR_RGBA_62.png │ ├── HDR_RGBA_63.png │ ├── HDR_RGBA_7.png │ ├── HDR_RGBA_8.png │ ├── HDR_RGBA_9.png │ ├── LDR_LLL1_0.png │ └── LDR_LLL1_1.png └── readme.md /code/apply_primary_shading.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), RootConstants(num32BitConstants=4, b0)" 2 | 3 | #include "constants.hlsl" 4 | #include "scene.hlsl" 5 | 6 | RWTexture2D LightTex: register(u0); 7 | RWTexture2D PositionTex: register(u1); 8 | RWTexture2D AlbedoTex: register(u2); 9 | RWTexture2D EmissionTex: register(u3); 10 | RWTexture2D RayDirTex: register(u4); 11 | 12 | struct context 13 | { 14 | float Time; 15 | float3 CamP; 16 | }; 17 | 18 | ConstantBuffer Context: register(b0); 19 | 20 | [RootSignature(RS)] 21 | [numthreads(32, 32, 1)] 22 | void main(uint2 ThreadId: SV_DispatchThreadID) 23 | { 24 | float3 P = PositionTex[ThreadId].xyz; 25 | float3 Rd = RayDirTex[ThreadId].xyz; 26 | float3 Sky = Env(Rd, Context.Time); 27 | 28 | if (length(P) < 10e30) 29 | { 30 | float3 Emission = EmissionTex[ThreadId].rgb; 31 | float3 Albedo = AlbedoTex[ThreadId].rgb; 32 | 33 | float3 Illumination = LightTex[ThreadId].rgb; 34 | float3 FirstBrdf = Albedo; 35 | float3 Radiance = FirstBrdf*Illumination + Emission; 36 | 37 | float Depth = length(Context.CamP - P); 38 | Radiance = lerp(Sky, Radiance, Fog(Depth)); 39 | 40 | LightTex[ThreadId] = float4(Radiance, 1.0); 41 | } 42 | else 43 | { 44 | LightTex[ThreadId] = float4(Sky, 1.0); 45 | } 46 | } -------------------------------------------------------------------------------- /code/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | IF NOT EXIST ..\build mkdir ..\build 4 | pushd ..\build 5 | 6 | ctime -begin sdf_engine.ctm 7 | cl -FC -nologo -Z7 -WX -W4 -wd4100 -wd4189 -wd4201 -wd4505 -wd4996 ..\code\main.cpp User32.lib D3D12.lib DXGI.lib D3DCompiler.lib 8 | 9 | ctime -end sdf_engine.ctm 10 | 11 | popd -------------------------------------------------------------------------------- /code/calc_variance.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), RootConstants(num32BitConstants=3, b0)" 2 | 3 | #include "math.hlsl" 4 | #include "edge_avoiding_functions.hlsl" 5 | 6 | RWTexture2D LumMomentTex: register(u0); 7 | RWTexture2D VarianceTex: register(u1); 8 | 9 | RWTexture2D InputTex: register(u2); 10 | RWTexture2D GBufferTex: register(u3); 11 | 12 | struct context 13 | { 14 | float3 CamP; 15 | }; 16 | 17 | ConstantBuffer Context: register(b0); 18 | 19 | [RootSignature(RS)] 20 | [numthreads(32, 32, 1)] 21 | void main(uint2 ThreadId: SV_DispatchThreadID) 22 | { 23 | float SampleCount = InputTex[ThreadId].a; 24 | 25 | if (SampleCount > 4) // enough temporal data to estimate variance 26 | { 27 | float2 Moments = LumMomentTex[ThreadId]; 28 | float Mean = Moments.x; 29 | float Mean2 = Moments.y; 30 | float Variance = abs(Mean2 - Mean*Mean); 31 | VarianceTex[ThreadId] = Variance; 32 | } 33 | else // spatial variance estimation fallback 34 | { 35 | float4 Geom = GBufferTex[ThreadId]; 36 | float3 CenterN = Geom.xyz; 37 | float CenterDepth = Geom.w; 38 | 39 | float Mean = 0; 40 | float Mean2 = 0; 41 | float TotalContrib = 0; 42 | for (int dY = -3; dY <= 3; ++dY) 43 | { 44 | for (int dX = -3; dX <= 3; ++dX) 45 | { 46 | int2 Coord = int2(ThreadId) + int2(dX, dY); 47 | float4 TapGeom = GBufferTex[Coord]; 48 | float3 Tap = InputTex[Coord].rgb; 49 | 50 | float3 TapN = TapGeom.xyz; 51 | float TapDepth = TapGeom.w; 52 | float TapLum = CalcLuminance(Tap); 53 | 54 | float W = DepthWeight(CenterDepth, TapDepth); 55 | W *= NormalWeight(CenterN, TapN); 56 | 57 | Mean += W * TapLum; 58 | Mean2 += W * TapLum*TapLum; 59 | TotalContrib += W; 60 | } 61 | } 62 | 63 | if (TotalContrib > 0.0) 64 | { 65 | Mean /= TotalContrib; 66 | Mean2 /= TotalContrib; 67 | } 68 | 69 | float Variance = abs(Mean2 - Mean*Mean); 70 | Variance *= 100.0; // boost to prevent underestimation 71 | VarianceTex[ThreadId] = Variance; 72 | } 73 | } -------------------------------------------------------------------------------- /code/ch_math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | typedef uint8_t u8; 7 | typedef uint16_t u16; 8 | typedef uint32_t u32; 9 | typedef uint64_t u64; 10 | 11 | typedef int8_t i8; 12 | typedef int16_t i16; 13 | typedef int32_t i32; 14 | typedef int64_t i64; 15 | 16 | typedef float f32; 17 | typedef double f64; 18 | 19 | typedef i32 b32; 20 | typedef bool b8; 21 | 22 | #define EPSILON 0.000001f 23 | #define F32Max FLT_MAX 24 | #define U32Max UINT_MAX 25 | #define Pi32 3.1415926f 26 | 27 | #define CH_ASSERT(X) do { if (!(X)) *(int *)0 = 0; } while (0) 28 | 29 | //NOTE(chen): put your own assert here 30 | #ifndef ASSERT 31 | #define ASSERT(X) CH_ASSERT(X) 32 | #endif 33 | 34 | struct v2 35 | { 36 | f32 X, Y; 37 | }; 38 | 39 | struct v2i 40 | { 41 | i32 X, Y; 42 | }; 43 | 44 | union v3 45 | { 46 | struct 47 | { 48 | f32 X; 49 | f32 Y; 50 | f32 Z; 51 | }; 52 | 53 | struct 54 | { 55 | f32 R; 56 | f32 G; 57 | f32 B; 58 | }; 59 | 60 | f32 Data[3]; 61 | }; 62 | 63 | struct v3i 64 | { 65 | i32 X, Y, Z; 66 | }; 67 | 68 | union v4 69 | { 70 | struct 71 | { 72 | f32 X, Y, Z, W; 73 | }; 74 | 75 | f32 Data[4]; 76 | }; 77 | 78 | struct mat4 79 | { 80 | f32 Data[4][4]; 81 | }; 82 | 83 | struct mat3 84 | { 85 | f32 Data[3][3]; 86 | }; 87 | 88 | struct quaternion 89 | { 90 | f32 X; 91 | f32 Y; 92 | f32 Z; 93 | f32 W; 94 | }; 95 | 96 | // 97 | // 98 | // Misc 99 | 100 | inline f32 101 | Min(f32 A, f32 B) 102 | { 103 | return A < B? A: B; 104 | } 105 | 106 | inline f32 107 | Max(f32 A, f32 B) 108 | { 109 | return A > B? A: B; 110 | } 111 | 112 | inline v3 113 | Min(v3 A, v3 B) 114 | { 115 | return {Min(A.X, B.X), Min(A.Y, B.Y), Min(A.Z, B.Z)}; 116 | } 117 | 118 | inline v3 119 | Max(v3 A, v3 B) 120 | { 121 | return {Max(A.X, B.X), Max(A.Y, B.Y), Max(A.Z, B.Z)}; 122 | } 123 | 124 | inline i32 125 | Ceil(f32 Float) 126 | { 127 | return (i32)ceilf(Float); 128 | } 129 | 130 | inline i32 131 | Round(f32 Float) 132 | { 133 | return (i32)roundf(Float); 134 | } 135 | 136 | inline i32 137 | Floor(f32 Float) 138 | { 139 | return (i32)floorf(Float); 140 | } 141 | 142 | inline v3i 143 | Floor(v3 V) 144 | { 145 | v3i Result = {}; 146 | Result.X = (i32)floorf(V.X); 147 | Result.Y = (i32)floorf(V.Y); 148 | Result.Z = (i32)floorf(V.Z); 149 | return Result; 150 | } 151 | 152 | inline f32 153 | Square(f32 A) 154 | { 155 | return A * A; 156 | } 157 | 158 | inline b32 159 | IsInRange(f32 Value, f32 Min, f32 Max) 160 | { 161 | return Value >= Min && Value <= Max; 162 | } 163 | 164 | inline f32 165 | Abs(f32 A) 166 | { 167 | return fabsf(A); 168 | } 169 | 170 | inline f32 171 | MaxF32(f32 A, f32 B) 172 | { 173 | return A > B? A: B; 174 | } 175 | 176 | inline f32 177 | MinF32(f32 A, f32 B) 178 | { 179 | return A < B? A: B; 180 | } 181 | 182 | inline i32 183 | WrapIndex(i32 Index, i32 HighCap) 184 | { 185 | Index %= HighCap; 186 | if (Index < 0) 187 | { 188 | Index += HighCap; 189 | } 190 | return Index; 191 | } 192 | 193 | inline i32 194 | Wrap(i32 A, i32 HighCap, i32 LowCap = 0) 195 | { 196 | if (LowCap == HighCap) 197 | { 198 | return LowCap; 199 | } 200 | 201 | ASSERT(LowCap < HighCap); 202 | i32 Range = HighCap - LowCap; 203 | while (A <= LowCap) 204 | { 205 | A += Range; 206 | } 207 | while (A >= HighCap) 208 | { 209 | A -= Range; 210 | } 211 | return A; 212 | } 213 | 214 | inline int 215 | DigitCount(int Number) 216 | { 217 | int DigitCount = 1; 218 | while (true) 219 | { 220 | Number /= 10; 221 | if (Number) 222 | { 223 | ++DigitCount; 224 | } 225 | else 226 | { 227 | break; 228 | } 229 | } 230 | 231 | return DigitCount; 232 | } 233 | 234 | inline int 235 | Power(int Base, int Exp) 236 | { 237 | int Result = Base; 238 | for (int I = 1; I < Exp; ++I) 239 | { 240 | Result *= Base; 241 | } 242 | return Result; 243 | } 244 | 245 | inline int 246 | GetFirstDigit(int Number) 247 | { 248 | if (DigitCount(Number) > 1) 249 | { 250 | int FirstDigit = Number / Power(10, (DigitCount(Number) - 1)); 251 | return FirstDigit; 252 | } 253 | return Number; 254 | } 255 | 256 | inline int 257 | GetDigit(int Number, int Digit) 258 | { 259 | for (int I = 0; I < Digit; ++I) 260 | { 261 | int FirstDigit = GetFirstDigit(Number); 262 | Number -= FirstDigit * Power(10, (DigitCount(Number) - 1)); 263 | } 264 | return GetFirstDigit(Number); 265 | } 266 | 267 | inline f32 268 | SquareRoot(f32 Float) 269 | { 270 | return (f32)sqrtf(Float); 271 | } 272 | 273 | inline i32 274 | RoundF32ToI32(f32 Value) 275 | { 276 | i32 Result = (i32)(roundf(Value)); 277 | return Result; 278 | } 279 | 280 | inline f32 281 | DegreeToRadian(f32 Degree) 282 | { 283 | f32 Result = Degree / 180.0f * Pi32; 284 | return Result; 285 | } 286 | 287 | template 288 | inline T 289 | Clamp(T Value, T Min, T Max) 290 | { 291 | if (Value < Min) 292 | { 293 | return Min; 294 | } 295 | else if (Value > Max) 296 | { 297 | return Max; 298 | } 299 | else 300 | { 301 | return Value; 302 | } 303 | } 304 | 305 | // 306 | // 307 | // Vector 308 | 309 | inline v2 310 | V2(f32 A, f32 B) 311 | { 312 | return {A, B}; 313 | } 314 | 315 | inline v2 316 | V2(f32 A) 317 | { 318 | return {A, A}; 319 | } 320 | 321 | inline v2 322 | V2(int A, int B) 323 | { 324 | return {(f32)A, (f32)B}; 325 | } 326 | 327 | inline v2 328 | V2(v3 V) 329 | { 330 | return {V.X, V.Y}; 331 | } 332 | 333 | inline v3 334 | V3(f32 Float) 335 | { 336 | v3 Result; 337 | 338 | Result.X = Float; 339 | Result.Y = Float; 340 | Result.Z = Float; 341 | 342 | return Result; 343 | } 344 | 345 | inline v3 346 | fmodf(v3 A, v3 B) 347 | { 348 | v3 Result; 349 | Result.X = fmodf(A.X, B.X); 350 | Result.Y = fmodf(A.Y, B.Y); 351 | Result.Z = fmodf(A.Z, B.Z); 352 | return Result; 353 | } 354 | 355 | inline v3 356 | V3(f32 A, f32 B, f32 C) 357 | { 358 | return {A, B, C}; 359 | } 360 | 361 | inline v3 362 | V3(v4 V) 363 | { 364 | v3 Result; 365 | 366 | Result.X = V.X; 367 | Result.Y = V.Y; 368 | Result.Z = V.Z; 369 | 370 | return Result; 371 | } 372 | 373 | inline v2 374 | CastToV2(v2i V) 375 | { 376 | v2 Result; 377 | Result = {(f32)V.X, (f32)V.Y}; 378 | return Result; 379 | } 380 | 381 | inline v2i 382 | operator-(v2i A, v2i B) 383 | { 384 | v2i Result = {A.X - B.X, A.Y - B.Y}; 385 | return Result; 386 | } 387 | 388 | inline void 389 | SwapV2(v2 *A, v2 *B) 390 | { 391 | v2 Temp = *A; 392 | *A = *B; 393 | *B = Temp; 394 | } 395 | 396 | inline void 397 | SwapF32(f32 *A, f32 *B) 398 | { 399 | f32 Temp = *A; 400 | *A = *B; 401 | *B = Temp; 402 | } 403 | 404 | inline v2 405 | operator+(v2 A, v2 B) 406 | { 407 | v2 Result = {A.X + B.X, A.Y + B.Y}; 408 | return Result; 409 | } 410 | 411 | inline v2 412 | operator-(v2 A, v2 B) 413 | { 414 | v2 Result = {A.X - B.X, A.Y - B.Y}; 415 | return Result; 416 | } 417 | 418 | inline v2 419 | operator*(f32 S, v2 A) 420 | { 421 | v2 Result = {A.X * S, A.Y * S}; 422 | return Result; 423 | } 424 | 425 | inline void 426 | operator+=(v2 &A, v2 B) 427 | { 428 | A = A + B; 429 | } 430 | 431 | inline void 432 | operator-=(v2 &A, v2 B) 433 | { 434 | A = A - B; 435 | } 436 | 437 | inline void 438 | operator*=(v2 &A, f32 B) 439 | { 440 | A = B * A; 441 | } 442 | 443 | inline v3 444 | ZeroV3() 445 | { 446 | return {}; 447 | } 448 | 449 | inline v3 450 | operator+(v3 A, v3 B) 451 | { 452 | v3 Result = {A.X + B.X, A.Y + B.Y, A.Z + B.Z}; 453 | return Result; 454 | } 455 | 456 | inline v3 457 | operator-(v3 A, v3 B) 458 | { 459 | v3 Result = {A.X - B.X, A.Y - B.Y, A.Z - B.Z}; 460 | return Result; 461 | } 462 | 463 | inline v3 464 | operator*(v3 A, v3 B) 465 | { 466 | v3 Result = {A.X * B.X, A.Y * B.Y, A.Z * B.Z}; 467 | return Result; 468 | } 469 | 470 | inline v3 471 | operator*(f32 S, v3 A) 472 | { 473 | v3 Result = {A.X * S, A.Y * S, A.Z * S}; 474 | return Result; 475 | } 476 | 477 | inline v3 478 | operator*(v3 A, f32 S) 479 | { 480 | v3 Result = {A.X * S, A.Y * S, A.Z * S}; 481 | return Result; 482 | } 483 | 484 | inline v3 485 | operator/(v3 A, f32 B) 486 | { 487 | v3 Result = V3(A.X / B, A.Y / B, A.Z / B); 488 | return Result; 489 | } 490 | 491 | inline v3 492 | operator/(v3 A, v3 B) 493 | { 494 | v3 Result = V3(A.X / B.X, A.Y / B.Y, A.Z / B.Z); 495 | return Result; 496 | } 497 | 498 | inline void 499 | operator*=(v3 &A, v3 B) 500 | { 501 | A = A * B; 502 | } 503 | 504 | inline void 505 | operator+=(v3 &A, v3 B) 506 | { 507 | A = A + B; 508 | } 509 | 510 | inline void 511 | operator-=(v3 &A, v3 B) 512 | { 513 | A = A - B; 514 | } 515 | 516 | inline void 517 | operator*=(v3 &A, f32 B) 518 | { 519 | A = A * B; 520 | } 521 | 522 | inline void 523 | operator/=(v3 &A, f32 B) 524 | { 525 | A = A / B; 526 | } 527 | 528 | inline void 529 | operator/=(v3 &A, v3 B) 530 | { 531 | A = A / B; 532 | } 533 | 534 | inline v4 535 | V4(f32 X, f32 Y, f32 Z) 536 | { 537 | v4 Result; 538 | 539 | Result.X = X; 540 | Result.Y = Y; 541 | Result.Z = Z; 542 | Result.W = 1.0f; 543 | 544 | return Result; 545 | } 546 | 547 | inline v4 548 | V4(v3 A) 549 | { 550 | v4 Result = V4(A.X, A.Y, A.Z); 551 | return Result; 552 | } 553 | 554 | inline v4 555 | ZeroV4() 556 | { 557 | v4 Result = V4(0.0f, 0.0f, 0.0f); 558 | return Result; 559 | } 560 | 561 | inline v4 562 | operator+(v4 A, v4 B) 563 | { 564 | v4 Result = V4(A.X + B.X, A.Y + B.Y, A.Z + B.Z); 565 | return Result; 566 | } 567 | 568 | inline v4 569 | operator-(v4 A, v4 B) 570 | { 571 | v4 Result = V4(A.X - B.X, A.Y - B.Y, A.Z - B.Z); 572 | return Result; 573 | } 574 | 575 | inline v4 576 | operator*(v4 A, f32 S) 577 | { 578 | v4 Result = V4(A.X * S, A.Y * S, A.Z * S); 579 | return Result; 580 | } 581 | 582 | inline v4 583 | operator/(v4 A, f32 S) 584 | { 585 | v4 Result = V4(A.X / S, A.Y / S, A.Z / S); 586 | return Result; 587 | } 588 | 589 | inline void 590 | operator+=(v4 &A, v4 B) 591 | { 592 | A = A + B; 593 | } 594 | 595 | inline void 596 | operator-=(v4 &A, v4 B) 597 | { 598 | A = A - B; 599 | } 600 | 601 | inline void 602 | operator*=(v4 &A, f32 B) 603 | { 604 | A = A * B; 605 | } 606 | 607 | inline void 608 | operator/=(v4 &A, f32 B) 609 | { 610 | A = A / B; 611 | } 612 | 613 | inline v4 614 | DivideByW(v4 V) 615 | { 616 | v4 Result = V; 617 | Result.X /= Result.W; 618 | Result.Y /= Result.W; 619 | Result.Z /= Result.W; 620 | Result.W /= Result.W; 621 | return Result; 622 | } 623 | 624 | inline v3 625 | operator-(v3 A) 626 | { 627 | return A * -1.0f; 628 | } 629 | 630 | inline v3 631 | YAxis() 632 | { 633 | return {0.0f, 1.0f, 0.0f}; 634 | } 635 | 636 | inline v3 637 | XAxis() 638 | { 639 | return {1.0f, 0.0f, 0.0f}; 640 | } 641 | 642 | inline v3 643 | ZAxis() 644 | { 645 | return {0.0f, 0.0f, 1.0f}; 646 | } 647 | 648 | inline f32 649 | Dot(v3 A, v3 B) 650 | { 651 | f32 Result = A.X * B.X + A.Y * B.Y + A.Z * B.Z; 652 | return Result; 653 | } 654 | 655 | inline f32 656 | Dot(v4 A, v4 B) 657 | { 658 | f32 Result = A.X * B.X + A.Y * B.Y + A.Z * B.Z; 659 | return Result; 660 | } 661 | 662 | inline f32 663 | Len(v2 A) 664 | { 665 | f32 Result = sqrtf(Square(A.X) + Square(A.Y)); 666 | return Result; 667 | } 668 | 669 | inline f32 670 | Len(v3 A) 671 | { 672 | f32 Result = sqrtf(Square(A.X) + Square(A.Y) + Square(A.Z)); 673 | return Result; 674 | } 675 | 676 | inline f32 677 | Len(v4 A) 678 | { 679 | f32 Result = sqrtf(Square(A.X) + Square(A.Y) + Square(A.Z)); 680 | return Result; 681 | } 682 | 683 | inline f32 684 | LenSquared(v3 A) 685 | { 686 | return Dot(A, A); 687 | } 688 | 689 | inline v3 690 | Normalize(v3 V) 691 | { 692 | f32 Epsilon = 0.000001f; 693 | 694 | v3 Result = V; 695 | 696 | f32 Length = Len(V); 697 | if (Length > Epsilon) 698 | { 699 | Result.X = V.X / Length; 700 | Result.Y = V.Y / Length; 701 | Result.Z = V.Z / Length; 702 | } 703 | 704 | return Result; 705 | } 706 | 707 | inline v4 708 | Normalize(v4 V) 709 | { 710 | f32 Epsilon = 0.000001f; 711 | 712 | v4 Result = V; 713 | 714 | f32 Length = Len(V); 715 | if (Length > Epsilon) 716 | { 717 | Result.X = V.X / Length; 718 | Result.Y = V.Y / Length; 719 | Result.Z = V.Z / Length; 720 | } 721 | 722 | return Result; 723 | } 724 | 725 | inline v3 726 | Cross(v3 A, v3 B) 727 | { 728 | v3 Result = {}; 729 | 730 | Result.X = A.Y * B.Z - A.Z * B.Y; 731 | Result.Y = A.Z * B.X - A.X * B.Z; 732 | Result.Z = A.X * B.Y - A.Y * B.X; 733 | 734 | return Result; 735 | } 736 | 737 | inline v4 738 | Cross(v4 A, v4 B) 739 | { 740 | f32 X = A.Y * B.Z - A.Z * B.Y; 741 | f32 Y = A.Z * B.X - A.X * B.Z; 742 | f32 Z = A.X * B.Y - A.Y * B.X; 743 | 744 | v4 Result = V4(X, Y, Z); 745 | return Result; 746 | } 747 | 748 | // 749 | // 750 | // Matrix 751 | 752 | inline mat4 753 | operator*(mat4 A, mat4 B) 754 | { 755 | mat4 Result = {}; 756 | 757 | for (i32 Row = 0; Row < 4; ++Row) 758 | { 759 | for (i32 Col = 0; Col < 4; ++Col) 760 | { 761 | for (i32 I = 0; I < 4; ++I) 762 | { 763 | Result.Data[Row][Col] += A.Data[Row][I] * B.Data[I][Col]; 764 | } 765 | } 766 | } 767 | 768 | return Result; 769 | } 770 | 771 | inline v4 772 | operator*(v4 B, mat4 A) 773 | { 774 | v4 Result = B; 775 | 776 | for (i32 Col = 0; Col < 4; ++Col) 777 | { 778 | f32 Sum = 0.0f; 779 | for (i32 I = 0; I < 4; ++I) 780 | { 781 | Sum += A.Data[I][Col] * B.Data[I]; 782 | } 783 | Result.Data[Col] = Sum; 784 | } 785 | 786 | return Result; 787 | } 788 | 789 | inline v3 790 | operator*(v3 B, mat3 A) 791 | { 792 | v3 Result = B; 793 | 794 | for (i32 Col = 0; Col < 3; ++Col) 795 | { 796 | f32 Sum = 0.0f; 797 | for (i32 I = 0; I < 3; ++I) 798 | { 799 | Sum += A.Data[I][Col] * B.Data[I]; 800 | } 801 | Result.Data[Col] = Sum; 802 | } 803 | 804 | return Result; 805 | } 806 | 807 | inline void 808 | operator*=(v4 &A, mat4 B) 809 | { 810 | A = A * B; 811 | } 812 | 813 | inline void 814 | operator*=(v3 &A, mat3 B) 815 | { 816 | A = A * B; 817 | } 818 | 819 | inline void 820 | operator*=(mat4 &A, mat4 B) 821 | { 822 | A = A * B; 823 | } 824 | 825 | inline mat3 826 | Mat3Identity() 827 | { 828 | mat3 Result = {}; 829 | 830 | Result.Data[0][0] = 1.0f; 831 | Result.Data[1][1] = 1.0f; 832 | Result.Data[2][2] = 1.0f; 833 | 834 | return Result; 835 | } 836 | 837 | inline mat3 838 | Mat3(f32 E00, f32 E01, f32 E02, 839 | f32 E10, f32 E11, f32 E12, 840 | f32 E20, f32 E21, f32 E22) 841 | { 842 | mat3 Result = {}; 843 | 844 | Result.Data[0][0] = E00; 845 | Result.Data[0][1] = E01; 846 | Result.Data[0][2] = E02; 847 | Result.Data[1][0] = E10; 848 | Result.Data[1][1] = E11; 849 | Result.Data[1][2] = E12; 850 | Result.Data[2][0] = E20; 851 | Result.Data[2][1] = E21; 852 | Result.Data[2][2] = E22; 853 | 854 | return Result; 855 | } 856 | 857 | inline mat4 858 | Mat4(f32 E00, f32 E01, f32 E02, f32 E03, 859 | f32 E10, f32 E11, f32 E12, f32 E13, 860 | f32 E20, f32 E21, f32 E22, f32 E23, 861 | f32 E30, f32 E31, f32 E32, f32 E33) 862 | { 863 | mat4 Result = {}; 864 | 865 | Result.Data[0][0] = E00; 866 | Result.Data[0][1] = E01; 867 | Result.Data[0][2] = E02; 868 | Result.Data[0][3] = E03; 869 | 870 | Result.Data[1][0] = E10; 871 | Result.Data[1][1] = E11; 872 | Result.Data[1][2] = E12; 873 | Result.Data[1][3] = E13; 874 | 875 | Result.Data[2][0] = E20; 876 | Result.Data[2][1] = E21; 877 | Result.Data[2][2] = E22; 878 | Result.Data[2][3] = E23; 879 | 880 | Result.Data[3][0] = E30; 881 | Result.Data[3][1] = E31; 882 | Result.Data[3][2] = E32; 883 | Result.Data[3][3] = E33; 884 | 885 | return Result; 886 | } 887 | 888 | inline mat3 889 | Mat3(mat4 Mat) 890 | { 891 | mat3 Result = Mat3Identity(); 892 | for (int Y = 0; Y < 3; ++Y) 893 | { 894 | for (int X = 0; X < 3; ++X) 895 | { 896 | Result.Data[Y][X] = Mat.Data[Y][X]; 897 | } 898 | } 899 | return Result; 900 | } 901 | 902 | inline mat4 903 | Mat4Identity() 904 | { 905 | mat4 Result = {}; 906 | 907 | Result.Data[0][0] = 1.0f; 908 | Result.Data[1][1] = 1.0f; 909 | Result.Data[2][2] = 1.0f; 910 | Result.Data[3][3] = 1.0f; 911 | 912 | return Result; 913 | } 914 | 915 | #if 1 916 | //TODO(chen): needs further testing 917 | inline mat3 918 | Inverse(mat3 A) 919 | { 920 | mat3 Result = Mat3Identity(); 921 | mat3 Augment = A; 922 | 923 | for (int C = 0; C < 3; ++C) 924 | { 925 | //set pivots to non-zeros 926 | if (Augment.Data[C][C] == 0.0f) 927 | { 928 | int Big = C; 929 | for (int R = 0; R < 3; ++R) 930 | { 931 | if (fabs(Augment.Data[R][C]) > fabs(Augment.Data[Big][C])) 932 | { 933 | Big = R; 934 | } 935 | } 936 | 937 | if (Big == C) 938 | { 939 | ASSERT(!"Singular matrix, cannot be inverted"); 940 | } 941 | 942 | for (int R = 0; R < 3; ++R) 943 | { 944 | SwapF32(&Augment.Data[C][R], &Augment.Data[Big][R]); 945 | SwapF32(&Result.Data[C][R], &Result.Data[Big][R]); 946 | } 947 | } 948 | 949 | //set non-pivots to zeros 950 | for (int R = 0; R < 3; ++R) 951 | { 952 | if (R != C) 953 | { 954 | f32 Coeff = Augment.Data[R][C] / Augment.Data[C][C]; 955 | if (Coeff != 0.0f) 956 | { 957 | for (int C2 = 0; C2 < 3; ++C2) 958 | { 959 | Augment.Data[R][C2] -= Coeff * Augment.Data[C][C2]; 960 | Result.Data[R][C2] -= Coeff * Result.Data[C][C2]; 961 | } 962 | 963 | //set it to 0 just to be sure 964 | Augment.Data[R][C] = 0.0f; 965 | } 966 | } 967 | } 968 | } 969 | 970 | //scale all pivots to 1 971 | for (int R = 0; R < 3; ++R) 972 | { 973 | for (int C = 0; C < 3; ++C) 974 | { 975 | Result.Data[R][C] /= Augment.Data[R][R]; 976 | } 977 | } 978 | 979 | return Result; 980 | } 981 | #endif 982 | 983 | inline mat4 984 | Inverse(mat4 A) 985 | { 986 | mat4 Result = Mat4Identity(); 987 | mat4 Augment = A; 988 | 989 | for (int C = 0; C < 4; ++C) 990 | { 991 | //set pivots to non-zeros 992 | if (Augment.Data[C][C] == 0.0f) 993 | { 994 | int Big = C; 995 | for (int R = 0; R < 4; ++R) 996 | { 997 | if (fabs(Augment.Data[R][C]) > fabs(Augment.Data[Big][C])) 998 | { 999 | Big = R; 1000 | } 1001 | } 1002 | 1003 | if (Big == C) 1004 | { 1005 | ASSERT(!"Singular matrix, cannot be inverted"); 1006 | } 1007 | 1008 | for (int R = 0; R < 4; ++R) 1009 | { 1010 | SwapF32(&Augment.Data[C][R], &Augment.Data[Big][R]); 1011 | SwapF32(&Result.Data[C][R], &Result.Data[Big][R]); 1012 | } 1013 | } 1014 | 1015 | //set non-pivots to zeros 1016 | for (int R = 0; R < 4; ++R) 1017 | { 1018 | if (R != C) 1019 | { 1020 | f32 Coeff = Augment.Data[R][C] / Augment.Data[C][C]; 1021 | if (Coeff != 0.0f) 1022 | { 1023 | for (int C2 = 0; C2 < 4; ++C2) 1024 | { 1025 | Augment.Data[R][C2] -= Coeff * Augment.Data[C][C2]; 1026 | Result.Data[R][C2] -= Coeff * Result.Data[C][C2]; 1027 | } 1028 | 1029 | //set it to 0 just to be sure 1030 | Augment.Data[R][C] = 0.0f; 1031 | } 1032 | } 1033 | } 1034 | } 1035 | 1036 | //scale all pivots to 1 1037 | for (int R = 0; R < 4; ++R) 1038 | { 1039 | for (int C = 0; C < 4; ++C) 1040 | { 1041 | Result.Data[R][C] /= Augment.Data[R][R]; 1042 | } 1043 | } 1044 | 1045 | return Result; 1046 | } 1047 | 1048 | inline mat4 1049 | Transpose(mat4 A) 1050 | { 1051 | mat4 Result = {}; 1052 | 1053 | for (int Y = 0; Y < 4; ++Y) 1054 | { 1055 | for (int X = 0; X < 4; ++X) 1056 | { 1057 | Result.Data[Y][X] = A.Data[X][Y]; 1058 | } 1059 | } 1060 | 1061 | return Result; 1062 | } 1063 | 1064 | inline mat4 1065 | Mat4Translate(f32 dX, f32 dY, f32 dZ) 1066 | { 1067 | mat4 Result = Mat4Identity(); 1068 | 1069 | Result.Data[3][0] = dX; 1070 | Result.Data[3][1] = dY; 1071 | Result.Data[3][2] = dZ; 1072 | 1073 | return Result; 1074 | } 1075 | 1076 | inline mat4 1077 | Mat4Translate(v3 dP) 1078 | { 1079 | return Mat4Translate(dP.X, dP.Y, dP.Z); 1080 | } 1081 | 1082 | inline mat4 1083 | Mat4Scale(f32 sX, f32 sY, f32 sZ) 1084 | { 1085 | mat4 Result = {}; 1086 | 1087 | Result.Data[0][0] = sX; 1088 | Result.Data[1][1] = sY; 1089 | Result.Data[2][2] = sZ; 1090 | Result.Data[3][3] = 1.0f; 1091 | 1092 | return Result; 1093 | } 1094 | 1095 | inline mat4 1096 | Mat4Scale(v3 Scale) 1097 | { 1098 | mat4 Result = {}; 1099 | 1100 | Result.Data[0][0] = Scale.X; 1101 | Result.Data[1][1] = Scale.Y; 1102 | Result.Data[2][2] = Scale.Z; 1103 | Result.Data[3][3] = 1.0f; 1104 | 1105 | return Result; 1106 | } 1107 | 1108 | inline mat4 1109 | Mat4Scale(f32 S) 1110 | { 1111 | return Mat4Scale(S, S, S); 1112 | } 1113 | 1114 | inline mat4 1115 | Mat4RotateAroundX(f32 Radians) 1116 | { 1117 | mat4 Result = Mat4Identity(); 1118 | 1119 | Result.Data[1][1] = cosf(Radians); 1120 | Result.Data[1][2] = sinf(Radians); 1121 | Result.Data[2][1] = -sinf(Radians); 1122 | Result.Data[2][2] = cosf(Radians); 1123 | 1124 | return Result; 1125 | } 1126 | 1127 | inline mat4 1128 | Mat4RotateAroundY(f32 Radians) 1129 | { 1130 | mat4 Result = Mat4Identity(); 1131 | 1132 | Result.Data[0][0] = cosf(Radians); 1133 | Result.Data[0][2] = -sinf(Radians); 1134 | Result.Data[2][0] = sinf(Radians); 1135 | Result.Data[2][2] = cosf(Radians); 1136 | 1137 | return Result; 1138 | } 1139 | 1140 | inline mat4 1141 | Mat4RotateAroundZ(f32 Radians) 1142 | { 1143 | mat4 Result = Mat4Identity(); 1144 | 1145 | Result.Data[0][0] = cosf(Radians); 1146 | Result.Data[0][1] = sinf(Radians); 1147 | Result.Data[1][0] = -sinf(Radians); 1148 | Result.Data[1][1] = cosf(Radians); 1149 | 1150 | return Result; 1151 | } 1152 | 1153 | inline mat4 1154 | Mat4Rotate(v3 Euler) 1155 | { 1156 | mat4 Result = {}; 1157 | 1158 | Result = Mat4RotateAroundX(Euler.X); 1159 | Result *= Mat4RotateAroundY(Euler.Y); 1160 | Result *= Mat4RotateAroundZ(Euler.Z); 1161 | 1162 | return Result; 1163 | } 1164 | 1165 | inline mat4 1166 | Mat4Ortho(float Left, float Right, float Bottom, float Top, float Near, float Far) 1167 | { 1168 | mat4 Result = {}; 1169 | 1170 | Result.Data[0][0] = 2.0f / (Right - Left); 1171 | Result.Data[1][1] = 2.0f / (Top - Bottom); 1172 | Result.Data[2][2] = 2.0f / (Far - Near); 1173 | Result.Data[3][0] = (Left + Right) / (Left - Right); 1174 | Result.Data[3][1] = (Bottom + Top) / (Bottom - Top); 1175 | Result.Data[3][2] = (Far + Near) / (Near - Far); 1176 | Result.Data[3][3] = 1.0f; 1177 | 1178 | return Result; 1179 | } 1180 | 1181 | inline mat4 1182 | Mat4Perspective(f32 FOV, f32 AspectRatio, f32 ZNear, f32 ZFar) 1183 | { 1184 | mat4 Result = {}; 1185 | 1186 | f32 HFOV = FOV / 2.0f; 1187 | 1188 | Result.Data[0][0] = 1.0f / (tanf(DegreeToRadian(HFOV)) * AspectRatio); 1189 | Result.Data[1][1] = 1.0f / tanf(DegreeToRadian(HFOV)); 1190 | Result.Data[2][3] = 1.0f; 1191 | Result.Data[2][2] = (ZFar + ZNear) / (ZFar - ZNear); 1192 | Result.Data[3][2] = -(2.0f *ZFar*ZNear) / (ZFar - ZNear); 1193 | 1194 | return Result; 1195 | } 1196 | 1197 | inline mat4 1198 | Mat4LookAt(v3 P, v3 Target) 1199 | { 1200 | v3 Up = YAxis(); 1201 | 1202 | v3 CamDirection = Normalize(Target - P); 1203 | v3 CamRight = Normalize(Cross(Up, CamDirection)); 1204 | v3 CamUp = Normalize(Cross(CamDirection, CamRight)); 1205 | 1206 | mat4 Result = Mat4Identity(); 1207 | Result.Data[0][0] = CamRight.X; 1208 | Result.Data[1][0] = CamRight.Y; 1209 | Result.Data[2][0] = CamRight.Z; 1210 | Result.Data[0][1] = CamUp.X; 1211 | Result.Data[1][1] = CamUp.Y; 1212 | Result.Data[2][1] = CamUp.Z; 1213 | Result.Data[0][2] = CamDirection.X; 1214 | Result.Data[1][2] = CamDirection.Y; 1215 | Result.Data[2][2] = CamDirection.Z; 1216 | 1217 | Result = Mat4Translate(-P.X, -P.Y, -P.Z) * Result; 1218 | return Result; 1219 | } 1220 | 1221 | inline v3 1222 | ExtractTranslation(mat4 Matrix) 1223 | { 1224 | v3 Translation = {}; 1225 | 1226 | Translation.X = Matrix.Data[3][0]; 1227 | Translation.Y = Matrix.Data[3][1]; 1228 | Translation.Z = Matrix.Data[3][2]; 1229 | 1230 | return Translation; 1231 | } 1232 | 1233 | inline quaternion 1234 | Quaternion() 1235 | { 1236 | quaternion Result = {}; 1237 | Result.W = 1.0f; 1238 | return Result; 1239 | } 1240 | 1241 | inline quaternion 1242 | Quaternion(v3 Axis, f32 Angle) 1243 | { 1244 | quaternion Result; 1245 | 1246 | Axis = Normalize(Axis); 1247 | Result.X = Axis.X * sinf(0.5f*Angle); 1248 | Result.Y = Axis.Y * sinf(0.5f*Angle); 1249 | Result.Z = Axis.Z * sinf(0.5f*Angle); 1250 | Result.W = cosf(0.5f*Angle); 1251 | 1252 | return Result; 1253 | } 1254 | 1255 | inline quaternion 1256 | Conjugate(quaternion A) 1257 | { 1258 | quaternion Result = A; 1259 | 1260 | Result.X = -Result.X; 1261 | Result.Y = -Result.Y; 1262 | Result.Z = -Result.Z; 1263 | 1264 | return Result; 1265 | } 1266 | 1267 | inline quaternion 1268 | operator*(quaternion A, quaternion B) 1269 | { 1270 | quaternion Result; 1271 | 1272 | v4 AV = V4(A.X, A.Y, A.Z); 1273 | v4 BV = V4(B.X, B.Y, B.Z); 1274 | 1275 | v4 VectorComponent = BV*A.W + AV*B.W + Cross(AV, BV); 1276 | f32 ScalarComponent = A.W*B.W - Dot(AV, BV); 1277 | 1278 | Result.X = VectorComponent.X; 1279 | Result.Y = VectorComponent.Y; 1280 | Result.Z = VectorComponent.Z; 1281 | Result.W = ScalarComponent; 1282 | 1283 | return Result; 1284 | } 1285 | 1286 | inline void 1287 | operator*=(quaternion &A, quaternion B) 1288 | { 1289 | A = A * B; 1290 | } 1291 | 1292 | inline quaternion 1293 | Quaternion(v3 V) 1294 | { 1295 | quaternion Result; 1296 | Result.X = V.X; 1297 | Result.Y = V.Y; 1298 | Result.Z = V.Z; 1299 | Result.W = 0.0f; 1300 | 1301 | return Result; 1302 | } 1303 | 1304 | inline v3 1305 | Rotate(v3 V, quaternion Q) 1306 | { 1307 | v3 Result; 1308 | 1309 | quaternion VectorAsQuaternion = Quaternion(V); 1310 | quaternion ResultInQuaternion = Q * VectorAsQuaternion * Conjugate(Q); 1311 | 1312 | Result.X = ResultInQuaternion.X; 1313 | Result.Y = ResultInQuaternion.Y; 1314 | Result.Z = ResultInQuaternion.Z; 1315 | 1316 | return Result; 1317 | } 1318 | 1319 | inline v4 1320 | Rotate(v4 Vector, quaternion Quaternion) 1321 | { 1322 | v4 Result; 1323 | 1324 | quaternion VectorAsQuaternion; 1325 | VectorAsQuaternion.X = Vector.X; 1326 | VectorAsQuaternion.Y = Vector.Y; 1327 | VectorAsQuaternion.Z = Vector.Z; 1328 | VectorAsQuaternion.W = 1.0f; 1329 | 1330 | quaternion ResultInQuaternion = Quaternion * VectorAsQuaternion * Conjugate(Quaternion); 1331 | 1332 | Result.X = ResultInQuaternion.X; 1333 | Result.Y = ResultInQuaternion.Y; 1334 | Result.Z = ResultInQuaternion.Z; 1335 | Result.W = Vector.W; 1336 | return Result; 1337 | } 1338 | 1339 | inline mat4 1340 | QuaternionToMat4(quaternion Q) 1341 | { 1342 | mat4 Result = Mat4Identity(); 1343 | 1344 | f32 SquareX = Square(Q.X); 1345 | f32 SquareY = Square(Q.Y); 1346 | f32 SquareZ = Square(Q.Z); 1347 | f32 XY = Q.X * Q.Y; 1348 | f32 XZ = Q.X * Q.Z; 1349 | f32 XW = Q.X * Q.W; 1350 | f32 YW = Q.Y * Q.W; 1351 | f32 YZ = Q.Y * Q.Z; 1352 | f32 ZW = Q.Z * Q.W; 1353 | 1354 | Result.Data[0][0] = 1.0f - 2.0f*SquareY - 2.0f*SquareZ; 1355 | Result.Data[0][1] = 2.0f*XY + 2.0f*ZW; 1356 | Result.Data[0][2] = 2.0f*XZ - 2.0f*YW; 1357 | Result.Data[1][0] = 2.0f*XY - 2.0f*ZW; 1358 | Result.Data[1][1] = 1.0f - 2*SquareX - 2.0f*SquareZ; 1359 | Result.Data[1][2] = 2.0f*YZ + 2.0f*XW; 1360 | Result.Data[2][0] = 2.0f*XZ + 2.0f*YW; 1361 | Result.Data[2][1] = 2.0f*YZ - 2.0f*XW; 1362 | Result.Data[2][2] = 1.0f - 2.0f*SquareX - 2.0f*SquareY; 1363 | 1364 | return Result; 1365 | } 1366 | 1367 | inline quaternion 1368 | Normalize(quaternion Q) 1369 | { 1370 | f32 Length = sqrtf(Square(Q.X) + Square(Q.Y) + Square(Q.Z) + Square(Q.W)); 1371 | 1372 | Q.X /= Length; 1373 | Q.Y /= Length; 1374 | Q.Z /= Length; 1375 | Q.W /= Length; 1376 | 1377 | return Q; 1378 | } 1379 | 1380 | inline quaternion 1381 | RotationToQuaternion(mat4 Mat) 1382 | { 1383 | quaternion Quat = {}; 1384 | 1385 | float tr, s, q[4]; 1386 | int i, j, k; 1387 | int nxt[3] = {1, 2, 0}; 1388 | tr = Mat.Data[0][0] + Mat.Data[1][1] + Mat.Data[2][2]; 1389 | // check the diagonal 1390 | if (tr > 0.0) { 1391 | s = (f32)sqrtf(tr + 1.0f); 1392 | Quat.W = s / 2.0f; 1393 | s = 0.5f / s; 1394 | Quat.X = (Mat.Data[1][2] - Mat.Data[2][1]) * s; 1395 | Quat.Y = (Mat.Data[2][0] - Mat.Data[0][2]) * s; 1396 | Quat.Z = (Mat.Data[0][1] - Mat.Data[1][0]) * s; 1397 | } else { 1398 | // diagonal is negative 1399 | i = 0; 1400 | if (Mat.Data[1][1] > Mat.Data[0][0]) i = 1; 1401 | if (Mat.Data[2][2] > Mat.Data[i][i]) i = 2; 1402 | j = nxt[i]; 1403 | k = nxt[j]; 1404 | s = (f32)sqrtf((Mat.Data[i][i] - (Mat.Data[j][j] + Mat.Data[k][k])) + 1.0f); 1405 | q[i] = s * 0.5f; 1406 | if (s != 0.0) s = 0.5f / s; 1407 | q[3] = (Mat.Data[j][k] - Mat.Data[k][j]) * s; 1408 | q[j] = (Mat.Data[i][j] + Mat.Data[j][i]) * s; 1409 | q[k] = (Mat.Data[i][k] + Mat.Data[k][i]) * s; 1410 | Quat.X = q[0]; 1411 | Quat.Y = q[1]; 1412 | Quat.Z = q[2]; 1413 | Quat.W = q[3]; 1414 | } 1415 | 1416 | return Quat; 1417 | } 1418 | 1419 | inline quaternion 1420 | operator*(f32 S, quaternion A) 1421 | { 1422 | quaternion Result; 1423 | 1424 | Result.X = A.X * S; 1425 | Result.Y = A.Y * S; 1426 | Result.Z = A.Z * S; 1427 | Result.W = A.W * S; 1428 | 1429 | return Result; 1430 | } 1431 | 1432 | inline quaternion 1433 | operator+(quaternion A, quaternion B) 1434 | { 1435 | quaternion Result; 1436 | 1437 | Result.X = A.X + B.X; 1438 | Result.Y = A.Y + B.Y; 1439 | Result.Z = A.Z + B.Z; 1440 | Result.W = A.W + B.W; 1441 | 1442 | return Result; 1443 | } 1444 | 1445 | inline f32 1446 | Dot(quaternion A, quaternion B) 1447 | { 1448 | return A.X * B.X + A.Y * B.Y + A.Z * B.Z + A.W * B.W; 1449 | } 1450 | 1451 | inline quaternion 1452 | Lerp(quaternion A, quaternion B, f32 T) 1453 | { 1454 | quaternion Result; 1455 | 1456 | Result.X = A.X * (1.0f - T) + B.X * T; 1457 | Result.Y = A.Y * (1.0f - T) + B.Y * T; 1458 | Result.Z = A.Z * (1.0f - T) + B.Z * T; 1459 | Result.W = A.W * (1.0f - T) + B.W * T; 1460 | 1461 | Result = Normalize(Result); 1462 | 1463 | return Result; 1464 | } 1465 | 1466 | inline quaternion 1467 | ShortestLerp(quaternion A, quaternion B, f32 T) 1468 | { 1469 | if (Dot(A, B) <= 0.0f) 1470 | { 1471 | A = -1.0f * A; 1472 | } 1473 | 1474 | return Lerp(A, B, T); 1475 | } 1476 | 1477 | inline quaternion 1478 | Slerp(quaternion A, quaternion B, f32 T) 1479 | { 1480 | f32 Theta = acosf(Dot(A, B)); 1481 | f32 STheta = sinf(Theta); 1482 | 1483 | f32 FirstWeight = sinf((1.0f - T) * Theta) / STheta; 1484 | f32 SecondWeight = sinf(T * Theta) / STheta; 1485 | 1486 | quaternion Result; 1487 | Result = FirstWeight * A + SecondWeight * B; 1488 | Result = Normalize(Result); 1489 | 1490 | return Result; 1491 | } 1492 | 1493 | inline f32 1494 | Lerp(f32 A, f32 B, f32 T) 1495 | { 1496 | return A * (1.0f - T) + B * T; 1497 | } 1498 | 1499 | inline v3 1500 | Lerp(v3 A, v3 B, f32 T) 1501 | { 1502 | v3 Result = A * (1.0f - T) + B * T; 1503 | 1504 | return Result; 1505 | } 1506 | 1507 | inline f32 1508 | Max(v3 V) 1509 | { 1510 | return Max(Max(V.X, V.Y), V.Z); 1511 | } 1512 | 1513 | inline v3 1514 | ApplyMat4(v3 V, mat4 Mat) 1515 | { 1516 | v4 _V = {V.X, V.Y, V.Z, 1.0f}; 1517 | _V *= Mat; 1518 | return V3(_V); 1519 | } 1520 | 1521 | inline b32 1522 | QuadraticIsSolvable(f32 A, f32 B, f32 C) 1523 | { 1524 | f32 SquareRootInnerTerm = B*B - 4.0f*A*C; 1525 | return (SquareRootInnerTerm >= 0.0f && A != 0.0f); 1526 | } 1527 | 1528 | inline v2 1529 | SolveForQuadraticRoots(f32 A, f32 B, f32 C) 1530 | { 1531 | v2 Result = {}; 1532 | ASSERT(QuadraticIsSolvable(A, B, C)); 1533 | 1534 | f32 SquareRootTerm = SquareRoot(B*B - 4.0f*A*C); 1535 | f32 FirstRoot = (-B + SquareRootTerm) / (2.0f * A); 1536 | f32 SecondRoot = (-B - SquareRootTerm) / (2.0f * A); 1537 | 1538 | Result.X = FirstRoot; 1539 | Result.Y = SecondRoot; 1540 | return Result; 1541 | } 1542 | 1543 | // pbrt's implementation 1544 | f32 RadicalInverse(u64 Index, int Base) 1545 | { 1546 | f32 Result = 0.0f; 1547 | 1548 | f32 InvBase = 1.0f / f32(Base); 1549 | u64 A = Index; 1550 | u64 ReversedDigits = 0; 1551 | f32 InvBaseN = 1.0f; 1552 | while (A) 1553 | { 1554 | u64 Next = A / Base; 1555 | u64 Digit = A - Next * Base; 1556 | ReversedDigits = ReversedDigits * Base + Digit; 1557 | InvBaseN *= InvBase; 1558 | A = Next; 1559 | } 1560 | 1561 | return ReversedDigits * InvBaseN; 1562 | } 1563 | -------------------------------------------------------------------------------- /code/clear.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0))" 2 | 3 | RWTexture2D OutputTex: register(u0); 4 | 5 | [RootSignature(RS)] 6 | [numthreads(32, 32, 1)] 7 | void main(uint2 ThreadID: SV_DispatchThreadID) 8 | { 9 | OutputTex[ThreadID] = 0.0; 10 | } -------------------------------------------------------------------------------- /code/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define internal static 4 | #define local_persist static 5 | #define global static 6 | #define ASSERT(Value) do { if (!(Value)) *(int *)0 = 0; } while (0) 7 | #define ARRAY_COUNT(Array) (sizeof(Array)/sizeof((Array)[0])) 8 | 9 | #include 10 | 11 | typedef uint8_t u8; 12 | typedef uint16_t u16; 13 | typedef uint32_t u32; 14 | typedef uint64_t u64; 15 | 16 | typedef int8_t i8; 17 | typedef int16_t i16; 18 | typedef int32_t i32; 19 | typedef int64_t i64; 20 | 21 | typedef bool b8; 22 | typedef i32 b32; 23 | 24 | typedef float f32; 25 | typedef double f64; 26 | -------------------------------------------------------------------------------- /code/compute_disocclusion.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), DescriptorTable(UAV(u5)), RootConstants(num32BitConstants=6, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D PositionTex: register(u0); 6 | RWTexture2D NormalTex: register(u1); 7 | RWTexture2D PositionHistTex: register(u2); 8 | RWTexture2D NormalHistTex: register(u3); 9 | RWTexture2D PrevPixelIdTex: register(u4); 10 | RWTexture2D DisocclusionTex: register(u5); 11 | 12 | struct context 13 | { 14 | int FrameIndex; 15 | float3 CamP; 16 | int Width; 17 | int Height; 18 | }; 19 | 20 | ConstantBuffer Context: register(b0); 21 | 22 | [RootSignature(RS)] 23 | [numthreads(32, 32, 1)] 24 | void main(uint2 ThreadId: SV_DispatchThreadID) 25 | { 26 | float3 P = PositionTex[ThreadId].xyz; 27 | float3 CurrN = NormalTex[ThreadId].xyz; 28 | float2 PrevPixelId = PrevPixelIdTex[ThreadId]; 29 | 30 | uint IsDisoccluded = 1; 31 | if (all(PrevPixelId >= 0.0 && PrevPixelId <= float2(Context.Width-1, Context.Height-1))) 32 | { 33 | float2 Interp = frac(PrevPixelId); 34 | int2 PrevPixelCoord = floor(PrevPixelId); 35 | 36 | float TotalContrib = 0; 37 | for (int dY = 0; dY <= 1; ++dY) 38 | { 39 | for (int dX = 0; dX <= 1; ++dX) 40 | { 41 | int2 TapCoord = PrevPixelCoord + int2(dX, dY); 42 | float3 PrevN = NormalHistTex[TapCoord].xyz; 43 | float3 PrevP = PositionHistTex[TapCoord].xyz; 44 | float PrevDepth = length(Context.CamP - PrevP); 45 | float CurrDepth = length(Context.CamP - P); 46 | if (abs(1.0 - CurrDepth/PrevDepth) < 0.1 && 47 | dot(CurrN, PrevN) >= 0.99) 48 | { 49 | float2 Bilinear = lerp(1.0-Interp, Interp, float2(dX, dY)); 50 | float W = Bilinear.x * Bilinear.y; 51 | TotalContrib += W; 52 | } 53 | } 54 | } 55 | 56 | if (TotalContrib > 0.0) 57 | { 58 | IsDisoccluded = 0; 59 | } 60 | } 61 | 62 | DisocclusionTex[ThreadId] = IsDisoccluded; 63 | } 64 | -------------------------------------------------------------------------------- /code/constants.hlsl: -------------------------------------------------------------------------------- 1 | #define T_MAX 1000.0 2 | #define Pi 3.1415926 3 | #define T_EPSILON 0.01 4 | #define MAX_RAYMARCH_ITER 256 5 | 6 | #define RAYMARCH_RELAXATION 1.6 -------------------------------------------------------------------------------- /code/coolbox.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | point_query Map(float3 P, float Time) 4 | { 5 | point_query Q; 6 | Q.Dist = 10e31; 7 | Q.MatId = -1; 8 | 9 | float Room = sdBox(P - float3(0, 4, 0), 5); 10 | Room = abs(Room)-0.01; 11 | float Neg = -sdBox(P - float3(5.0, 3.0, 0), float3(1, 1, 4)); 12 | //Room = max(Room, Neg); 13 | 14 | float Shape = sdBox(P, 1); 15 | Shape = abs(Shape) - 0.03; 16 | Shape = max(Shape, dot(P, float3(1,1,-1))-1.3); 17 | Shape = 0.5 * Shape; 18 | 19 | BIND(Room, 0); 20 | BIND(Shape, 1); 21 | 22 | return Q; 23 | } 24 | 25 | material MapMaterial(int MatId, float3 P, float Time) 26 | { 27 | material Mat; 28 | 29 | if (MatId == 0) // room 30 | { 31 | Mat.Albedo = 0.8; 32 | Mat.Emission = 0; 33 | if (abs(P.y-4.0) < 0.3) Mat.Emission = 2; 34 | } 35 | else if (MatId == 1) // obj 36 | { 37 | Mat.Albedo = float3(0.8, 0.4, 0.3); 38 | Mat.Emission = 0; 39 | 40 | //if (abs(P.y-3.0) < 0.1) Mat.Emission = 100; 41 | } 42 | else // invalid id (default) 43 | { 44 | Mat.Albedo = 0.8; 45 | Mat.Emission = 0.0; 46 | } 47 | 48 | return Mat; 49 | } 50 | 51 | float3 Env(float3 Rd, float Time) 52 | { 53 | return 1.00*float3(0.3, 0.4, 0.5); 54 | } 55 | 56 | float Fog(float Depth) 57 | { 58 | return 1.0; 59 | } 60 | 61 | float3 SunDir() 62 | { 63 | return normalize(float3(-0.2, 0.3, 0.1)); 64 | } 65 | 66 | float3 SunLight() 67 | { 68 | return float3(3.0, 3.0, 3.0); 69 | } 70 | 71 | float SunAperture() 72 | { 73 | return 0.1; 74 | } -------------------------------------------------------------------------------- /code/cornellbox.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | float sdBox1(float3 P) 4 | { 5 | P -= float3(1, -1.4, -0.5); 6 | P.xz = mul(P.xz, Rotate2(-0.3)); 7 | float Dist = sdBox(P, float3(0.6, 0.6, 0.6)); 8 | return Dist; 9 | } 10 | 11 | float sdBox2(float3 P) 12 | { 13 | P -= float3(-0.7, -0.7, 0.4); 14 | P.xz = mul(P.xz, Rotate2(0.3)); 15 | float Dist = sdBox(P, float3(0.6, 1.3, 0.6)); 16 | return Dist; 17 | } 18 | 19 | point_query Map(float3 P, float Time) 20 | { 21 | point_query Q; 22 | Q.Dist = 10e31; 23 | Q.MatId = -1; 24 | 25 | float Container = sdBox(P + float3(0, 0, 1.5), float3(2, 2, 3)); 26 | Container = abs(Container) - 0.01; 27 | float Neg = dot(P-float3(0, 0, -1.5), float3(0, 0, -1)); 28 | Container = max(Container, Neg); 29 | 30 | float Box1 = sdBox1(P); 31 | float Box2 = sdBox2(P); 32 | 33 | BIND(Container, 0); 34 | BIND(Box1, 1); 35 | BIND(Box2, 2); 36 | 37 | return Q; 38 | } 39 | 40 | material MapMaterial(int MatId, float3 P, float Time) 41 | { 42 | material Mat; 43 | 44 | if (MatId == 0) // container 45 | { 46 | Mat.Albedo = 1.0; 47 | if (P.x <= -1.95) Mat.Albedo = float3(1.0, 0.1, 0.1); 48 | if (P.x >= 1.95) Mat.Albedo = float3(0.1, 1.0, 0.1); 49 | 50 | Mat.Emission = 0; 51 | 52 | bool IsInSquare = max(abs(P.x), abs(P.z)) < 0.6; 53 | if (P.y > 0.0 && IsInSquare) Mat.Emission = 10; 54 | } 55 | else if (MatId == 1) 56 | { 57 | Mat.Albedo = 1; 58 | Mat.Emission = 0; 59 | } 60 | else if (MatId == 2) 61 | { 62 | Mat.Albedo = 1; 63 | Mat.Emission = 0; 64 | } 65 | else // nothing 66 | { 67 | Mat.Albedo = 0; 68 | Mat.Emission = 0; 69 | } 70 | 71 | return Mat; 72 | } 73 | 74 | float3 Env(float3 Rd, float Time) 75 | { 76 | return 0; 77 | } 78 | 79 | float Fog(float Depth) 80 | { 81 | return 1.0; 82 | } 83 | 84 | float3 SunDir() 85 | { 86 | return normalize(float3(-0.2, 0.3, 0.1)); 87 | } 88 | 89 | float3 SunLight() 90 | { 91 | return 0; 92 | } 93 | 94 | float SunAperture() 95 | { 96 | return 0.1; 97 | } -------------------------------------------------------------------------------- /code/correlate_history.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), RootConstants(num32BitConstants=14, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D PositionTex: register(u0); 6 | RWTexture2D PrevPixelIdTex: register(u1); 7 | 8 | struct context 9 | { 10 | float3 PrevCamP; 11 | uint Pad1; 12 | 13 | float4 PrevCamInvQuat; 14 | 15 | int Width; 16 | int Height; 17 | int FrameIndex; 18 | uint Pad2; 19 | 20 | float2 PixelOffset; 21 | }; 22 | 23 | ConstantBuffer Context: register(b0); 24 | 25 | [RootSignature(RS)] 26 | [numthreads(32, 32, 1)] 27 | void main(uint2 ThreadId: SV_DispatchThreadID) 28 | { 29 | float3 P = PositionTex[ThreadId].xyz; 30 | float3 PrevViewP = CalcViewP(P, Context.PrevCamP, Context.PrevCamInvQuat); 31 | float2 PrevUV = PrevViewP.xy / PrevViewP.z * 1.7; 32 | PrevUV.y = -PrevUV.y; 33 | PrevUV.x /= float(Context.Width) / float(Context.Height); 34 | float2 PrevPixelId = (0.5 * PrevUV + 0.5) * float2(Context.Width, 35 | Context.Height); 36 | PrevPixelId -= 0.5; 37 | PrevPixelId -= Context.PixelOffset; 38 | 39 | PrevPixelIdTex[ThreadId] = PrevPixelId; 40 | } -------------------------------------------------------------------------------- /code/d3d12_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #define STB_IMAGE_IMPLEMENTATION 4 | #include "stb_image.h" 5 | 6 | #define DXOP(Value) ASSERT(SUCCEEDED(Value)) 7 | 8 | struct descriptor 9 | { 10 | D3D12_CPU_DESCRIPTOR_HANDLE CPUHandle; 11 | D3D12_GPU_DESCRIPTOR_HANDLE GPUHandle; 12 | 13 | b32 IsValid(); 14 | }; 15 | 16 | struct descriptor_arena 17 | { 18 | ID3D12Device *D; 19 | 20 | ID3D12DescriptorHeap *Heap; 21 | D3D12_DESCRIPTOR_HEAP_TYPE HeapType; 22 | int Count; 23 | int Capacity; 24 | 25 | descriptor PushDescriptor(); 26 | }; 27 | 28 | struct pso 29 | { 30 | ID3D12PipelineState *Handle; 31 | ID3D12RootSignature *RootSignature; 32 | 33 | void Release(); 34 | }; 35 | 36 | struct texture 37 | { 38 | ID3D12Resource *Handle; 39 | D3D12_RESOURCE_STATES ResourceState; 40 | 41 | descriptor UAV; 42 | descriptor SRV; 43 | descriptor RTV; 44 | }; 45 | 46 | struct frame_context 47 | { 48 | ID3D12CommandAllocator *CmdAllocator; 49 | UINT64 FenceValue; 50 | ID3D12Fence *Fence; 51 | HANDLE FenceEvent; 52 | }; 53 | 54 | struct gpu_context 55 | { 56 | ID3D12Device *Device; 57 | ID3D12GraphicsCommandList *CmdList; 58 | ID3D12CommandQueue *CmdQueue; 59 | 60 | frame_context *Frames; 61 | int FramesInFlight; 62 | 63 | UINT BI; 64 | 65 | D3D12_RESOURCE_BARRIER CachedBarriers[16]; 66 | int CachedBarrierCount; 67 | 68 | void Reset(UINT BackbufferIndex); 69 | void WaitForGpu(UINT NextBackbufferIndex); 70 | void FlushFramesInFlight(); 71 | 72 | void UAVBarrier(texture *Tex); 73 | void TransitionBarrier(texture *Tex, D3D12_RESOURCE_STATES NewState); 74 | void FlushBarriers(); 75 | void CopyResourceBarriered(texture *Dest, texture *Source); 76 | 77 | void Upload(texture *Tex, void *Data); 78 | texture LoadTexture2D(char *Filename, DXGI_FORMAT Format, 79 | D3D12_RESOURCE_FLAGS Flags, 80 | D3D12_RESOURCE_STATES ResourceStates); 81 | 82 | void _PushToBarrierCache(D3D12_RESOURCE_BARRIER Barrier); 83 | }; 84 | 85 | // 86 | // 87 | // GPU context 88 | 89 | internal frame_context 90 | InitFrameContext(ID3D12Device *D) 91 | { 92 | frame_context Context = {}; 93 | 94 | DXOP(D->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, 95 | IID_PPV_ARGS(&Context.CmdAllocator))); 96 | 97 | DXOP(D->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Context.Fence))); 98 | Context.FenceEvent = CreateEventA(0, 0, FALSE, 0); 99 | 100 | return Context; 101 | } 102 | 103 | internal gpu_context 104 | InitGPUContext(ID3D12Device *D, int FramesInFlight) 105 | { 106 | gpu_context Context = {}; 107 | Context.Device = D; 108 | Context.FramesInFlight = FramesInFlight; 109 | 110 | D3D12_COMMAND_QUEUE_DESC CmdQueueDesc = {}; 111 | CmdQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 112 | CmdQueueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; 113 | CmdQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 114 | DXOP(D->CreateCommandQueue(&CmdQueueDesc, IID_PPV_ARGS(&Context.CmdQueue))); 115 | 116 | Context.Frames = (frame_context *)calloc(FramesInFlight, sizeof(frame_context)); 117 | for (int FrameI = 0; FrameI < Context.FramesInFlight; ++FrameI) 118 | { 119 | Context.Frames[FrameI] = InitFrameContext(D); 120 | } 121 | 122 | DXOP(D->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, 123 | Context.Frames[0].CmdAllocator, 0, 124 | IID_PPV_ARGS(&Context.CmdList))); 125 | DXOP(Context.CmdList->Close()); 126 | 127 | return Context; 128 | } 129 | 130 | void 131 | gpu_context::Reset(UINT CurrentBackbufferIndex) 132 | { 133 | this->BI = CurrentBackbufferIndex; 134 | 135 | DXOP(Frames[BI].CmdAllocator->Reset()); 136 | DXOP(CmdList->Reset(Frames[BI].CmdAllocator, 0)); 137 | } 138 | 139 | void 140 | gpu_context::WaitForGpu(UINT NextBI) 141 | { 142 | Frames[BI].FenceValue += 1; 143 | DXOP(CmdQueue->Signal(Frames[BI].Fence, Frames[BI].FenceValue)); 144 | 145 | frame_context *NextFrame = Frames + NextBI; 146 | 147 | if (NextFrame->Fence->GetCompletedValue() < NextFrame->FenceValue) 148 | { 149 | DXOP(NextFrame->Fence->SetEventOnCompletion(NextFrame->FenceValue, NextFrame->FenceEvent)); 150 | WaitForSingleObject(NextFrame->FenceEvent, INFINITE); 151 | } 152 | } 153 | 154 | void 155 | gpu_context::FlushFramesInFlight() 156 | { 157 | for (int BufferI = 0; BufferI < FramesInFlight; ++BufferI) 158 | { 159 | frame_context *Frame = Frames + BufferI; 160 | 161 | if (Frame->Fence->GetCompletedValue() < Frame->FenceValue) 162 | { 163 | DXOP(Frame->Fence->SetEventOnCompletion(Frame->FenceValue, Frame->FenceEvent)); 164 | WaitForSingleObject(Frame->FenceEvent, INFINITE); 165 | } 166 | } 167 | } 168 | 169 | void 170 | gpu_context::UAVBarrier(texture *Tex) 171 | { 172 | D3D12_RESOURCE_BARRIER Barrier = {}; 173 | Barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; 174 | Barrier.UAV.pResource = Tex->Handle; 175 | 176 | _PushToBarrierCache(Barrier); 177 | } 178 | 179 | void 180 | gpu_context::TransitionBarrier(texture *Tex, D3D12_RESOURCE_STATES NewState) 181 | { 182 | D3D12_RESOURCE_BARRIER Barrier = {}; 183 | Barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; 184 | Barrier.Transition.pResource = Tex->Handle; 185 | Barrier.Transition.StateBefore = Tex->ResourceState; 186 | Barrier.Transition.StateAfter = NewState; 187 | 188 | _PushToBarrierCache(Barrier); 189 | 190 | Tex->ResourceState = NewState; 191 | } 192 | 193 | void 194 | gpu_context::_PushToBarrierCache(D3D12_RESOURCE_BARRIER Barrier) 195 | { 196 | CachedBarriers[CachedBarrierCount++] = Barrier; 197 | 198 | if (CachedBarrierCount == ARRAY_COUNT(CachedBarriers)) 199 | { 200 | FlushBarriers(); 201 | } 202 | } 203 | 204 | void 205 | gpu_context::FlushBarriers() 206 | { 207 | CmdList->ResourceBarrier(CachedBarrierCount, CachedBarriers); 208 | CachedBarrierCount = 0; 209 | } 210 | 211 | void 212 | gpu_context::CopyResourceBarriered(texture *Dest, texture *Source) 213 | { 214 | D3D12_RESOURCE_STATES SourceState = Source->ResourceState; 215 | D3D12_RESOURCE_STATES DestState = Dest->ResourceState; 216 | TransitionBarrier(Source, D3D12_RESOURCE_STATE_COPY_SOURCE); 217 | TransitionBarrier(Dest, D3D12_RESOURCE_STATE_COPY_DEST); 218 | FlushBarriers(); 219 | 220 | CmdList->CopyResource(Dest->Handle, Source->Handle); 221 | 222 | TransitionBarrier(Source, SourceState); 223 | TransitionBarrier(Dest, DestState); 224 | FlushBarriers(); 225 | } 226 | 227 | // 228 | // 229 | // swapchain 230 | 231 | internal IDXGISwapChain3 * 232 | CreateSwapChain(ID3D12CommandQueue *CmdQueue, 233 | HWND Window, 234 | int BackbufferCount) 235 | { 236 | IDXGISwapChain3 *SwapChain = 0; 237 | 238 | IDXGIFactory2 *Factory2 = 0; 239 | DXOP(CreateDXGIFactory2(0, IID_PPV_ARGS(&Factory2))); 240 | 241 | IDXGISwapChain1 *SwapChain1 = 0; 242 | DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = {}; 243 | SwapChainDesc.Width = 0; // uses window width 244 | SwapChainDesc.Height = 0; // uses window height 245 | SwapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 246 | SwapChainDesc.Stereo = FALSE; 247 | SwapChainDesc.SampleDesc.Count = 1; // Single sample per pixel 248 | SwapChainDesc.SampleDesc.Quality = 0; 249 | SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 250 | SwapChainDesc.BufferCount = BackbufferCount; 251 | SwapChainDesc.Scaling = DXGI_SCALING_STRETCH; 252 | SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 253 | SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; 254 | SwapChainDesc.Flags = 0; 255 | DXOP(Factory2->CreateSwapChainForHwnd(CmdQueue, // for d3d12, must be command queue instead of device 256 | Window, 257 | &SwapChainDesc, 258 | 0, // doesn't support fullscreen 259 | 0, 260 | &SwapChain1)); 261 | DXOP(SwapChain1->QueryInterface(IID_PPV_ARGS(&SwapChain))); 262 | 263 | //NOTE(chen): tells windows to get the fuck out of my business 264 | // I will handle resize/fullscreen myself goddamn it 265 | DXOP(Factory2->MakeWindowAssociation(Window, DXGI_MWA_NO_WINDOW_CHANGES|DXGI_MWA_NO_ALT_ENTER|DXGI_MWA_NO_PRINT_SCREEN)); 266 | 267 | return SwapChain; 268 | } 269 | 270 | // 271 | // 272 | // descriptors 273 | 274 | b32 275 | descriptor::IsValid() 276 | { 277 | return GPUHandle.ptr != 0; 278 | } 279 | 280 | internal descriptor_arena 281 | InitDescriptorArena(ID3D12Device *D, int Count, 282 | D3D12_DESCRIPTOR_HEAP_TYPE HeapType) 283 | { 284 | descriptor_arena Arena = {}; 285 | Arena.D = D; 286 | Arena.Capacity = Count; 287 | Arena.HeapType = HeapType; 288 | 289 | D3D12_DESCRIPTOR_HEAP_DESC HeapDesc = {}; 290 | HeapDesc.Type = HeapType; 291 | HeapDesc.NumDescriptors = Count; 292 | if (HeapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) 293 | { 294 | HeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 295 | } 296 | else 297 | { 298 | HeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 299 | } 300 | DXOP(D->CreateDescriptorHeap(&HeapDesc, IID_PPV_ARGS(&Arena.Heap))); 301 | 302 | return Arena; 303 | } 304 | 305 | descriptor 306 | descriptor_arena::PushDescriptor() 307 | { 308 | ASSERT(Count < Capacity); 309 | 310 | descriptor Descriptor = {}; 311 | 312 | D3D12_CPU_DESCRIPTOR_HANDLE CPUHeapStart = Heap->GetCPUDescriptorHandleForHeapStart(); 313 | D3D12_GPU_DESCRIPTOR_HANDLE GPUHeapStart = Heap->GetGPUDescriptorHandleForHeapStart(); 314 | UINT DescriptorSize = D->GetDescriptorHandleIncrementSize(HeapType); 315 | 316 | int CurrIndex = Count; 317 | Descriptor.GPUHandle = GPUHeapStart; 318 | Descriptor.GPUHandle.ptr += CurrIndex * DescriptorSize; 319 | Descriptor.CPUHandle = CPUHeapStart; 320 | Descriptor.CPUHandle.ptr += CurrIndex * DescriptorSize; 321 | 322 | Count += 1; 323 | 324 | return Descriptor; 325 | } 326 | 327 | // 328 | // 329 | // pipeline states 330 | 331 | void pso::Release() 332 | { 333 | Handle->Release(); 334 | RootSignature->Release(); 335 | Handle = 0; 336 | RootSignature = 0; 337 | } 338 | 339 | struct file 340 | { 341 | void *Data; 342 | size_t Size; 343 | }; 344 | 345 | file ReadEntireFile(char *Path, int Padding) 346 | { 347 | file File = {}; 348 | 349 | FILE *F = fopen(Path, "rb"); 350 | if (F) 351 | { 352 | fseek(F, 0, SEEK_END); 353 | File.Size = ftell(F); 354 | rewind(F); 355 | File.Data = calloc(File.Size+Padding, 1); 356 | fread(File.Data, 1, File.Size, F); 357 | fclose(F); 358 | } 359 | 360 | return File; 361 | } 362 | 363 | file ReadTextFile(char *Path) 364 | { 365 | return ReadEntireFile(Path, 1); 366 | } 367 | 368 | file ReadBinaryFile(char *Path) 369 | { 370 | return ReadEntireFile(Path, 0); 371 | } 372 | 373 | internal ID3DBlob * 374 | CompileShader(file File, char *Filename, char *EntryPoint, char *Target, 375 | b32 *HasError_Out) 376 | { 377 | bool Failed = false; 378 | 379 | //NOTE(chen): for whatever reason, d3d compiler's preprocessor is really 380 | // bad at fetching include files. Numerously times it fails to 381 | // open include files when the file are already there ... 382 | // 383 | // this especially poses a problem with shader hotloading 384 | int MaxAttempt = 10; 385 | int Attempts = 0; 386 | ID3DBlob *CodeBlob; 387 | ID3DBlob *ErrorBlob; 388 | while (FAILED(D3DCompile(File.Data, File.Size, Filename, 389 | 0, D3D_COMPILE_STANDARD_FILE_INCLUDE, 390 | EntryPoint, Target, 0, 0, 391 | &CodeBlob, &ErrorBlob))) 392 | { 393 | if (Attempts == MaxAttempt) 394 | { 395 | Failed = true; 396 | break; 397 | } 398 | 399 | ErrorBlob->Release(); 400 | Sleep(1); // try wait for file to come back online .... 401 | Attempts += 1; 402 | } 403 | 404 | *HasError_Out = Failed; 405 | return Failed? ErrorBlob: CodeBlob; 406 | } 407 | 408 | internal ID3D12RootSignature * 409 | ReflectRootSignature(ID3D12Device *D, ID3DBlob *CodeBlob, 410 | ID3DBlob **RSErrorBlob_Out) 411 | { 412 | ID3D12RootSignature *Result = 0; 413 | 414 | ID3D12RootSignatureDeserializer *RSExtractor = 0; 415 | DXOP(D3D12CreateRootSignatureDeserializer(CodeBlob->GetBufferPointer(), 416 | CodeBlob->GetBufferSize(), IID_PPV_ARGS(&RSExtractor))); 417 | const D3D12_ROOT_SIGNATURE_DESC *RSDesc = RSExtractor->GetRootSignatureDesc(); 418 | 419 | ID3DBlob *RSBlob = 0; 420 | ID3DBlob *RSErrorBlob = 0; 421 | if (SUCCEEDED(D3D12SerializeRootSignature(RSDesc, 422 | D3D_ROOT_SIGNATURE_VERSION_1, 423 | &RSBlob, &RSErrorBlob))) 424 | { 425 | DXOP(D->CreateRootSignature(0, RSBlob->GetBufferPointer(), 426 | RSBlob->GetBufferSize(), 427 | IID_PPV_ARGS(&Result))); 428 | RSBlob->Release(); 429 | } 430 | else 431 | { 432 | *RSErrorBlob_Out = RSErrorBlob; 433 | } 434 | 435 | return Result; 436 | } 437 | 438 | internal pso 439 | InitComputePSO(ID3D12Device *D, char *Filename, char *EntryPoint) 440 | { 441 | pso PSO = {}; 442 | 443 | file File = ReadTextFile(Filename); 444 | if (!File.Data) 445 | { 446 | Win32Panic("Failed to load shader file: %s", Filename); 447 | return PSO; 448 | } 449 | 450 | b32 HasError = 0; 451 | ID3DBlob *Blob = CompileShader(File, Filename, EntryPoint, "cs_5_1", &HasError); 452 | free(File.Data); 453 | if (HasError) 454 | { 455 | Win32Panic("Shader compile error: %s", Blob->GetBufferPointer()); 456 | Blob->Release(); 457 | return PSO; 458 | } 459 | 460 | ID3DBlob *CodeBlob = Blob; 461 | 462 | ID3DBlob *RSErrorBlob = 0; 463 | PSO.RootSignature = ReflectRootSignature(D, CodeBlob, &RSErrorBlob); 464 | if (!PSO.RootSignature) 465 | { 466 | char *ErrorMsg = (char *)RSErrorBlob->GetBufferPointer(); 467 | Win32Panic("Root Signature serialization error: %s", ErrorMsg); 468 | RSErrorBlob->Release(); 469 | } 470 | 471 | D3D12_COMPUTE_PIPELINE_STATE_DESC PSODesc = {}; 472 | PSODesc.pRootSignature = PSO.RootSignature; 473 | PSODesc.CS = {CodeBlob->GetBufferPointer(), CodeBlob->GetBufferSize()}; 474 | DXOP(D->CreateComputePipelineState(&PSODesc, IID_PPV_ARGS(&PSO.Handle))); 475 | 476 | CodeBlob->Release(); 477 | 478 | return PSO; 479 | } 480 | 481 | typedef void edit_graphics_pso(D3D12_GRAPHICS_PIPELINE_STATE_DESC *PSODesc); 482 | 483 | internal pso 484 | InitGraphicsPSO(ID3D12Device *D, char *Filename, 485 | D3D12_INPUT_ELEMENT_DESC *InputElements, int InputElementCount, 486 | edit_graphics_pso *EditGraphicsPSO = 0) 487 | { 488 | pso PSO = {}; 489 | 490 | file File = ReadTextFile(Filename); 491 | if (!File.Data) 492 | { 493 | Win32Panic("Failed to load shader file: %s", Filename); 494 | return PSO; 495 | } 496 | 497 | b32 HasError = 0; 498 | ID3DBlob *VSBlob = CompileShader(File, Filename, "VS", "vs_5_1", &HasError); 499 | if (HasError) 500 | { 501 | Win32Panic("VS Shader compile error: %s", VSBlob->GetBufferPointer()); 502 | VSBlob->Release(); 503 | return PSO; 504 | } 505 | 506 | ID3DBlob *PSBlob = CompileShader(File, Filename, "PS", "ps_5_1", &HasError); 507 | if (HasError) 508 | { 509 | Win32Panic("PS Shader compile error: %s", PSBlob->GetBufferPointer()); 510 | PSBlob->Release(); 511 | return PSO; 512 | } 513 | 514 | free(File.Data); 515 | 516 | ID3DBlob *RSErrorBlob = 0; 517 | PSO.RootSignature = ReflectRootSignature(D, VSBlob, &RSErrorBlob); 518 | if (!PSO.RootSignature) 519 | { 520 | char *ErrorMsg = (char *)RSErrorBlob->GetBufferPointer(); 521 | Win32Panic("Root Signature serialization error: %s", ErrorMsg); 522 | RSErrorBlob->Release(); 523 | } 524 | 525 | D3D12_GRAPHICS_PIPELINE_STATE_DESC PSODesc = {}; 526 | PSODesc.pRootSignature = PSO.RootSignature; 527 | PSODesc.VS = {VSBlob->GetBufferPointer(), VSBlob->GetBufferSize()}; 528 | PSODesc.PS = {PSBlob->GetBufferPointer(), PSBlob->GetBufferSize()}; 529 | PSODesc.BlendState.AlphaToCoverageEnable = FALSE; 530 | PSODesc.BlendState.IndependentBlendEnable = FALSE; 531 | PSODesc.BlendState.RenderTarget[0].BlendEnable = FALSE; 532 | PSODesc.BlendState.RenderTarget[0].LogicOpEnable = FALSE; 533 | PSODesc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; 534 | PSODesc.SampleMask = UINT_MAX; 535 | PSODesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; 536 | PSODesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; 537 | PSODesc.RasterizerState.FrontCounterClockwise = TRUE; 538 | PSODesc.DepthStencilState.DepthEnable = FALSE; 539 | PSODesc.DepthStencilState.StencilEnable = FALSE; 540 | PSODesc.InputLayout.pInputElementDescs = InputElements; 541 | PSODesc.InputLayout.NumElements = InputElementCount; 542 | PSODesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; 543 | PSODesc.NumRenderTargets = 1; 544 | PSODesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; 545 | PSODesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; 546 | PSODesc.SampleDesc.Count = 1; // one sample per pixel 547 | PSODesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; 548 | if (EditGraphicsPSO) 549 | { 550 | EditGraphicsPSO(&PSODesc); 551 | } 552 | 553 | DXOP(D->CreateGraphicsPipelineState(&PSODesc, IID_PPV_ARGS(&PSO.Handle))); 554 | 555 | VSBlob->Release(); 556 | PSBlob->Release(); 557 | 558 | return PSO; 559 | } 560 | 561 | // 562 | // 563 | // boilerplates 564 | 565 | internal D3D12_HEAP_PROPERTIES 566 | DefaultHeapProps(D3D12_HEAP_TYPE Type) 567 | { 568 | D3D12_HEAP_PROPERTIES HeapProps = {}; 569 | HeapProps.Type = Type; 570 | HeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 571 | HeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; 572 | return HeapProps; 573 | } 574 | 575 | // 576 | // 577 | // buffers 578 | 579 | internal ID3D12Resource * 580 | InitBuffer(ID3D12Device *D, size_t ByteCount, 581 | D3D12_HEAP_TYPE HeapType, 582 | D3D12_RESOURCE_STATES InitialState, 583 | D3D12_RESOURCE_FLAGS Flags) 584 | { 585 | ID3D12Resource *Buffer = 0; 586 | 587 | D3D12_HEAP_PROPERTIES HeapProps = DefaultHeapProps(HeapType); 588 | D3D12_RESOURCE_DESC ResourceDesc = {}; 589 | ResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; 590 | ResourceDesc.Width = ByteCount; 591 | ResourceDesc.Height = 1; 592 | ResourceDesc.DepthOrArraySize = 1; 593 | ResourceDesc.MipLevels = 1; 594 | ResourceDesc.Format = DXGI_FORMAT_UNKNOWN; 595 | ResourceDesc.SampleDesc = {1, 0}; 596 | ResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; 597 | ResourceDesc.Flags = Flags; 598 | 599 | DXOP(D->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE, 600 | &ResourceDesc, InitialState, 0, 601 | IID_PPV_ARGS(&Buffer))); 602 | 603 | return Buffer; 604 | } 605 | 606 | internal void 607 | Upload(ID3D12Resource *Buffer, void *Data, size_t ByteCount) 608 | { 609 | D3D12_RANGE ReadRange = {}; 610 | void *MappedAddr = 0; 611 | DXOP(Buffer->Map(0, &ReadRange, &MappedAddr)); 612 | memcpy(MappedAddr, Data, ByteCount); 613 | D3D12_RANGE WriteRange = {0, ByteCount}; 614 | Buffer->Unmap(0, &WriteRange); 615 | } 616 | 617 | // 618 | // 619 | // textures 620 | 621 | internal texture 622 | WrapTexture(ID3D12Resource *Handle, D3D12_RESOURCE_STATES ResourceState) 623 | { 624 | texture Tex = {}; 625 | Tex.ResourceState = ResourceState; 626 | Tex.Handle = Handle; 627 | return Tex; 628 | } 629 | 630 | internal texture 631 | InitTexture2D(ID3D12Device *D, int Width, int Height, DXGI_FORMAT Format, 632 | D3D12_RESOURCE_FLAGS Flags, D3D12_RESOURCE_STATES ResourceState) 633 | { 634 | texture Tex = {}; 635 | Tex.ResourceState = ResourceState; 636 | 637 | D3D12_HEAP_PROPERTIES HeapProps = DefaultHeapProps(D3D12_HEAP_TYPE_DEFAULT); 638 | D3D12_RESOURCE_DESC ResourceDesc = {}; 639 | ResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 640 | ResourceDesc.Width = Width; 641 | ResourceDesc.Height = Height; 642 | ResourceDesc.DepthOrArraySize = 1; 643 | ResourceDesc.MipLevels = 1; 644 | ResourceDesc.Format = Format; 645 | ResourceDesc.SampleDesc = {1, 0}; 646 | ResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 647 | ResourceDesc.Flags = Flags; 648 | DXOP(D->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE, 649 | &ResourceDesc, ResourceState, 0, 650 | IID_PPV_ARGS(&Tex.Handle))); 651 | 652 | return Tex; 653 | } 654 | 655 | int GetChannelCount(DXGI_FORMAT Format) 656 | { 657 | int ChannelCount = 0; 658 | 659 | switch (Format) 660 | { 661 | case DXGI_FORMAT_R8G8B8A8_UNORM: 662 | { 663 | ChannelCount = 4; 664 | } break; 665 | 666 | case DXGI_FORMAT_R8_UNORM: 667 | { 668 | ChannelCount = 1; 669 | } break; 670 | 671 | default: 672 | { 673 | ASSERT(!"unhandled DXGI format"); 674 | } break; 675 | } 676 | 677 | return ChannelCount; 678 | } 679 | 680 | int GetBytesPerTexel(DXGI_FORMAT Format) 681 | { 682 | int Count = 0; 683 | 684 | switch (Format) 685 | { 686 | case DXGI_FORMAT_R8G8B8A8_UNORM: 687 | { 688 | Count = 4; 689 | } break; 690 | 691 | case DXGI_FORMAT_R8_UNORM: 692 | { 693 | Count = 1; 694 | } break; 695 | 696 | default: 697 | { 698 | ASSERT(!"unhandled DXGI format"); 699 | } break; 700 | } 701 | 702 | return Count; 703 | } 704 | 705 | void gpu_context::Upload(texture *Tex, void *TexData) 706 | { 707 | D3D12_RESOURCE_DESC Desc = Tex->Handle->GetDesc(); 708 | int Width = int(Desc.Width); 709 | int Height = int(Desc.Height); 710 | DXGI_FORMAT Format = Desc.Format; 711 | 712 | D3D12_PLACED_SUBRESOURCE_FOOTPRINT Footprint = {}; 713 | 714 | Device->GetCopyableFootprints(&Desc, 0, 1, 0, &Footprint, 0, 0, 0); 715 | int RowPitch = Footprint.Footprint.RowPitch; 716 | int BytesPerPixel = GetBytesPerTexel(Format); 717 | int BytesPerRow = BytesPerPixel * Width; 718 | 719 | size_t PaddedTexDataSize = Height * RowPitch; 720 | u8 *PaddedTexData = (u8 *)calloc(PaddedTexDataSize, 1); 721 | 722 | u8 *Reader = (u8 *)TexData; 723 | u8 *Writer = PaddedTexData; 724 | for (int Y = 0; Y < Height; ++Y) 725 | { 726 | for (int X = 0; X < BytesPerRow; ++X) 727 | { 728 | Writer[X] = Reader[X]; 729 | } 730 | Writer += RowPitch; 731 | Reader += BytesPerRow; 732 | } 733 | 734 | ID3D12Resource *StagingBuffer = InitBuffer(Device, 735 | PaddedTexDataSize, 736 | D3D12_HEAP_TYPE_UPLOAD, 737 | D3D12_RESOURCE_STATE_GENERIC_READ, 738 | D3D12_RESOURCE_FLAG_NONE); 739 | ::Upload(StagingBuffer, PaddedTexData, PaddedTexDataSize); 740 | 741 | Reset(0); 742 | 743 | D3D12_TEXTURE_COPY_LOCATION DestLocation = {}; 744 | DestLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; 745 | DestLocation.pResource = Tex->Handle; 746 | DestLocation.SubresourceIndex = 0; 747 | 748 | D3D12_TEXTURE_COPY_LOCATION SourceLocation = {}; 749 | SourceLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; 750 | SourceLocation.pResource = StagingBuffer; 751 | SourceLocation.PlacedFootprint = Footprint; 752 | 753 | D3D12_RESOURCE_STATES TexState = Tex->ResourceState; 754 | 755 | TransitionBarrier(Tex, D3D12_RESOURCE_STATE_COPY_DEST); 756 | FlushBarriers(); 757 | 758 | CmdList->CopyTextureRegion(&DestLocation, 0, 0, 0, &SourceLocation, 0); 759 | 760 | TransitionBarrier(Tex, TexState); 761 | FlushBarriers(); 762 | 763 | DXOP(CmdList->Close()); 764 | ID3D12CommandList *CmdLists[] = {CmdList}; 765 | CmdQueue->ExecuteCommandLists(1, CmdLists); 766 | WaitForGpu(0); 767 | 768 | StagingBuffer->Release(); 769 | free(PaddedTexData); 770 | } 771 | 772 | texture gpu_context::LoadTexture2D(char *Filename, DXGI_FORMAT Format, 773 | D3D12_RESOURCE_FLAGS Flags, D3D12_RESOURCE_STATES ResourceState) 774 | { 775 | int Width, Height, ChannelCount; 776 | u8 *ImageData = stbi_load(Filename, &Width, &Height, &ChannelCount, 0); 777 | ASSERT(ChannelCount == GetChannelCount(Format)); 778 | 779 | texture Tex = InitTexture2D(Device, Width, Height, Format, Flags, ResourceState); 780 | Upload(&Tex, ImageData); 781 | 782 | stbi_image_free(ImageData); 783 | 784 | return Tex; 785 | } 786 | 787 | internal void 788 | AssignUAV(ID3D12Device *D, texture *Tex, descriptor_arena *Arena) 789 | { 790 | Tex->UAV = Arena->PushDescriptor(); 791 | D->CreateUnorderedAccessView(Tex->Handle, 0, 0, 792 | Tex->UAV.CPUHandle); 793 | } 794 | 795 | internal void 796 | AssignSRV(ID3D12Device *D, texture *Tex, descriptor_arena *Arena) 797 | { 798 | Tex->SRV = Arena->PushDescriptor(); 799 | D->CreateShaderResourceView(Tex->Handle, 0, Tex->SRV.CPUHandle); 800 | } 801 | 802 | internal void 803 | AssignRTV(ID3D12Device *D, texture *Tex, descriptor_arena *Arena) 804 | { 805 | Tex->RTV = Arena->PushDescriptor(); 806 | D->CreateRenderTargetView(Tex->Handle, 0, Tex->RTV.CPUHandle); 807 | } 808 | -------------------------------------------------------------------------------- /code/demo.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | point_query Map(float3 P, float Time) 4 | { 5 | point_query Q; 6 | Q.Dist = 10e31; 7 | Q.MatId = -1; 8 | 9 | float Floor = P.y + 1.0; 10 | float Sphere = length(P) - 1.0; 11 | float Sphere2 = length(P - float3(0, 3, 2)) - 1.0; 12 | 13 | BIND(Floor, 0); 14 | BIND(Sphere, 1); 15 | BIND(Sphere2, 2); 16 | 17 | return Q; 18 | } 19 | 20 | material MapMaterial(int MatId, float3 P, float Time) 21 | { 22 | material Mat; 23 | Mat.Albedo = 0.9; 24 | Mat.Emission = 0; 25 | 26 | if (MatId == 0) // floor 27 | { 28 | Mat.Albedo = 0.5; 29 | } 30 | 31 | return Mat; 32 | } 33 | 34 | float3 Env(float3 Rd, float Time) 35 | { 36 | float3 Sky = 0.1 * float3(0.3, 0.4, 0.5); 37 | return Sky; 38 | } 39 | 40 | float Fog(float Depth) 41 | { 42 | return 1.0; 43 | } 44 | 45 | float3 SunDir() 46 | { 47 | return normalize(float3(0.2, 0.3, 0.1)); 48 | } 49 | 50 | float3 SunLight() 51 | { 52 | return 1.0; 53 | } 54 | 55 | float SunAperture() 56 | { 57 | return 0.1; 58 | } -------------------------------------------------------------------------------- /code/edge_avoiding_functions.hlsl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | float DepthWeight(float CenterDepth, float SampleDepth) 4 | { 5 | return abs(1.0 - CenterDepth/SampleDepth) < 0.1? 1.0: 0.0; 6 | } 7 | 8 | float NormalWeight(float3 CenterNormal, float3 SampleNormal) 9 | { 10 | return pow(max(0.0, dot(CenterNormal, SampleNormal)), 32.0); 11 | } 12 | 13 | float LumWeight(float4 CenterCol, float4 SampleCol, float StdDeviation) 14 | { 15 | float CenterLum = CalcLuminance(CenterCol.rgb); 16 | float SampleLum = CalcLuminance(SampleCol.rgb); 17 | 18 | float Epsilon = 0.0000001; 19 | float Alpha = 4.0; 20 | return exp(-abs(CenterLum - SampleLum) / (Alpha*StdDeviation + Epsilon)); 21 | } 22 | -------------------------------------------------------------------------------- /code/interior.hlsl: -------------------------------------------------------------------------------- 1 | float hash(in float x) 2 | { 3 | return frac(sin(13.15*x)*932.3); 4 | } 5 | 6 | float box(float3 p, float3 b) 7 | { 8 | float3 d = abs(p) - b; 9 | return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); 10 | } 11 | 12 | #define GROUND_ID 0 13 | #define LIGHT_BULB_ID 1 14 | #define TABLE_ID 2 15 | #define ROOM_ID 3 16 | 17 | point_query Map(float3 p, float Time) 18 | { 19 | float light_bulb = length(p - float3(4.25, 4.2, 0)) - 2.0; 20 | float ground = p.y; 21 | float room = max(-box(p - float3(1.8, 1, 3), float3(0.5, 0.9, 1)), max(box(p - float3(0, 1, 0), float3(3, 2, 2.7)), -box(p - float3(0, 1, 0), 0.8*float3(3, 2, 3)))); 22 | float table = box(p - float3(1.9, 0.6, 0.8), float3(0.5, 0.05, 0.6)); 23 | float leg1 = box(p - float3(1.45, 0.3, 1.35), float3(0.05, 0.3, 0.05)); 24 | float leg2 = box(p - float3(2.35, 0.3, 1.35), float3(0.05, 0.3, 0.05)); 25 | float leg3 = box(p - float3(1.45, 0.3, 0.25), float3(0.05, 0.3, 0.05)); 26 | float leg4 = box(p - float3(2.35, 0.3, 0.25), float3(0.05, 0.3, 0.05)); 27 | table = min(min(min(min(table, leg1), leg2), leg3), leg4); 28 | float d = min(table, min(room, min(light_bulb, ground))); 29 | 30 | int id = -1; 31 | if (d == light_bulb) 32 | { 33 | id = LIGHT_BULB_ID; 34 | } 35 | else if (d == ground) 36 | { 37 | id = GROUND_ID; 38 | } 39 | else if (d == table) 40 | { 41 | id = TABLE_ID; 42 | } 43 | else if (d == room) 44 | { 45 | id = ROOM_ID; 46 | } 47 | 48 | point_query Q; 49 | Q.Dist = d; 50 | Q.MatId = id; 51 | return Q; 52 | } 53 | 54 | float hash3(in float3 p) 55 | { 56 | return hash(dot(p, float3(91.3, 151.16, 72.15))); 57 | } 58 | 59 | float noise(in float3 p) 60 | { 61 | float3 ipos = floor(p); 62 | float3 fpos = frac(p); 63 | 64 | float a = hash3(ipos + float3(0, 0, 0)); 65 | float b = hash3(ipos + float3(1, 0, 0)); 66 | float c = hash3(ipos + float3(0, 1, 0)); 67 | float d = hash3(ipos + float3(1, 1, 0)); 68 | float e = hash3(ipos + float3(0, 0, 1)); 69 | float f = hash3(ipos + float3(1, 0, 1)); 70 | float g = hash3(ipos + float3(0, 1, 1)); 71 | float h = hash3(ipos + float3(1, 1, 1)); 72 | 73 | float3 t = smoothstep(0, 1, fpos); 74 | 75 | return lerp(lerp(lerp(a, b, t.x), lerp(c, d, t.x), t.y), 76 | lerp(lerp(e, f, t.x), lerp(g, h, t.x), t.y), 77 | t.z); 78 | } 79 | 80 | float fbm(in float3 p) 81 | { 82 | float res = 0.0; 83 | float amp = 0.5; 84 | float freq = 2.0; 85 | for (int i = 0; i < 6; ++i) 86 | { 87 | res += amp*noise(freq*p); 88 | amp *= 0.5; 89 | freq *= 2.0; 90 | } 91 | return res; 92 | } 93 | 94 | material MapMaterial(int id, float3 p, float Time) 95 | { 96 | material mat; 97 | 98 | if (id == GROUND_ID) 99 | { 100 | if (dot(p,p) > 3.5*3.5) 101 | { 102 | mat.Albedo = 0.5; 103 | mat.Emission = 0; 104 | } 105 | else 106 | { 107 | float3 col1 = float3(0.33, 0.18, 0.09); 108 | float3 col2 = 0.3*float3(0.33, 0.18, 0.09); 109 | 110 | mat.Albedo = lerp(col1, col2, fbm(float3(p.x, p.y, 10.0*p.z))); 111 | mat.Emission = 0; 112 | } 113 | } 114 | else if (id == LIGHT_BULB_ID) 115 | { 116 | mat.Albedo = 0.5; 117 | mat.Emission = 0.000001; 118 | } 119 | else if (id == ROOM_ID) 120 | { 121 | float3 col1 = float3(0.8, 0.4, 0.2); 122 | float3 col2 = 0.7*col1; 123 | 124 | mat.Albedo = lerp(col1, col2, fbm(p)); 125 | mat.Emission = 0; 126 | } 127 | else if (id == TABLE_ID) 128 | { 129 | mat.Albedo = 0.9; 130 | mat.Emission = 0; 131 | } 132 | else 133 | { 134 | mat.Albedo = float3(1, 0, 1); 135 | mat.Emission = 0; 136 | } 137 | 138 | return mat; 139 | } 140 | 141 | float3 Env(float3 Rd, float Time) 142 | { 143 | return 10.0; 144 | } 145 | 146 | float Fog(float Depth) 147 | { 148 | return 1.0; 149 | } 150 | 151 | float3 SunDir() 152 | { 153 | return normalize(float3(-0.2, 0.3, 0.1)); 154 | } 155 | 156 | float3 SunLight() 157 | { 158 | return 0; 159 | } 160 | 161 | float SunAperture() 162 | { 163 | return 0.1; 164 | } -------------------------------------------------------------------------------- /code/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sdf_engine.cpp" 2 | 3 | #include 4 | #include 5 | #include "ShellScalingAPI.h" 6 | #include 7 | #include 8 | 9 | global bool gAppIsDone; 10 | global int gClientWidth = 1280; 11 | global int gClientHeight = 720; 12 | global input gInput; 13 | 14 | internal void 15 | Win32MessageBox(char *Title, UINT Type, char *Fmt, ...) 16 | { 17 | va_list Args; 18 | va_start(Args, Fmt); 19 | 20 | char Buf[2048]; 21 | vsnprintf(Buf, sizeof(Buf), Fmt, Args); 22 | va_end(Args); 23 | 24 | MessageBoxA(0, Buf, Title, Type); 25 | } 26 | 27 | internal void 28 | Win32Panic(char *Fmt, ...) 29 | { 30 | va_list Args; 31 | va_start(Args, Fmt); 32 | 33 | char Buf[2048]; 34 | vsnprintf(Buf, sizeof(Buf), Fmt, Args); 35 | va_end(Args); 36 | 37 | MessageBoxA(0, Buf, "Panic", MB_OK|MB_ICONERROR); 38 | 39 | ExitProcess(1); 40 | } 41 | 42 | global WINDOWPLACEMENT GlobalWindowPosition; 43 | internal void 44 | Win32ToggleFullscreen(HWND Window) 45 | { 46 | DWORD Style = GetWindowLong(Window, GWL_STYLE); 47 | if (Style & WS_OVERLAPPEDWINDOW) 48 | { 49 | MONITORINFO mi = { sizeof(mi) }; 50 | if (GetWindowPlacement(Window, &GlobalWindowPosition) && 51 | GetMonitorInfo(MonitorFromWindow(Window, MONITOR_DEFAULTTOPRIMARY), &mi)) 52 | { 53 | SetWindowLong(Window, GWL_STYLE, 54 | Style & ~WS_OVERLAPPEDWINDOW); 55 | SetWindowPos(Window, HWND_TOP, 56 | mi.rcMonitor.left, mi.rcMonitor.top, 57 | mi.rcMonitor.right - mi.rcMonitor.left, 58 | mi.rcMonitor.bottom - mi.rcMonitor.top, 59 | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); 60 | } 61 | } 62 | else 63 | { 64 | SetWindowLong(Window, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW); 65 | SetWindowPlacement(Window, &GlobalWindowPosition); 66 | SetWindowPos(Window, NULL, 0, 0, 0, 0, 67 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | 68 | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); 69 | } 70 | } 71 | 72 | LRESULT CALLBACK 73 | Win32WindowCallback(HWND Window, UINT Message, 74 | WPARAM WParam, LPARAM LParam) 75 | { 76 | LRESULT Result = 0; 77 | 78 | switch (Message) 79 | { 80 | case WM_CLOSE: 81 | case WM_QUIT: 82 | { 83 | gAppIsDone = true; 84 | } break; 85 | 86 | case WM_KEYDOWN: 87 | case WM_SYSKEYDOWN: 88 | case WM_KEYUP: 89 | case WM_SYSKEYUP: 90 | { 91 | b32 KeyIsDown = (LParam & (1 << 31)) == 0; 92 | b32 KeyWasDown = (LParam & (1 << 30)) != 0; 93 | b32 AltIsDown = (LParam & (1 << 29)) != 0; 94 | 95 | if (KeyIsDown) 96 | { 97 | gInput.Keys[WParam] += 1; 98 | 99 | // prevent overflow 100 | if (gInput.Keys[WParam] == 1000) 101 | { 102 | gInput.Keys[WParam] = 1000; 103 | } 104 | } 105 | else 106 | { 107 | gInput.Keys[WParam] = 0; 108 | } 109 | 110 | if (KeyWasDown != KeyIsDown) 111 | { 112 | if (KeyIsDown) 113 | { 114 | if (WParam == VK_ESCAPE) 115 | { 116 | gAppIsDone = true; 117 | } 118 | 119 | if (AltIsDown && WParam == VK_F4) 120 | { 121 | gAppIsDone = true; 122 | } 123 | 124 | if (AltIsDown && WParam == VK_RETURN) 125 | { 126 | Win32ToggleFullscreen(Window); 127 | } 128 | } 129 | } 130 | } break; 131 | 132 | case WM_MOUSEMOVE: 133 | { 134 | v2i OldMouseP = gInput.MouseP; 135 | 136 | gInput.MouseP.X = GET_X_LPARAM(LParam); 137 | gInput.MouseP.Y = GET_Y_LPARAM(LParam); 138 | 139 | if (gInput.MouseDataIsInitialized) 140 | { 141 | gInput.MousedP = gInput.MouseP - OldMouseP; 142 | } 143 | else 144 | { 145 | gInput.MouseDataIsInitialized = true; 146 | } 147 | } break; 148 | 149 | case WM_LBUTTONDOWN: 150 | { 151 | gInput.MouseDown = true; 152 | } break; 153 | 154 | case WM_LBUTTONUP: 155 | { 156 | gInput.MouseDown = false; 157 | } break; 158 | 159 | case WM_ACTIVATE: 160 | { 161 | bool IsDeactivated = LOWORD(WParam) == WA_INACTIVE; 162 | 163 | if (IsDeactivated) 164 | { 165 | for (int KeyI = 0; KeyI < ARRAY_COUNT(gInput.Keys); ++KeyI) 166 | { 167 | gInput.Keys[KeyI] = 0; 168 | } 169 | 170 | gInput.MouseDown = false; 171 | } 172 | } break; 173 | 174 | case WM_MOUSELEAVE: 175 | { 176 | gInput.MouseDown = false; 177 | } break; 178 | 179 | case WM_SIZE: 180 | { 181 | gClientWidth = LOWORD(LParam); 182 | gClientHeight = HIWORD(LParam); 183 | } break; 184 | 185 | default: 186 | { 187 | Result = DefWindowProc(Window, Message, WParam, LParam); 188 | } break; 189 | } 190 | 191 | return Result; 192 | } 193 | 194 | internal FILETIME 195 | GetLastWriteTime(char *FilePath) 196 | { 197 | FILETIME Result = {}; 198 | 199 | HANDLE SceneFile = CreateFileA(FilePath, 200 | GENERIC_READ, 201 | FILE_SHARE_READ|FILE_SHARE_WRITE, 202 | 0, OPEN_EXISTING, 0, 0); 203 | if (SceneFile != INVALID_HANDLE_VALUE) 204 | { 205 | GetFileTime(SceneFile, 0, 0, &Result); 206 | CloseHandle(SceneFile); 207 | } 208 | 209 | return Result; 210 | } 211 | 212 | struct file_tracker 213 | { 214 | FILETIME LastWriteTime; 215 | char Path[256]; 216 | }; 217 | 218 | global file_tracker gFileTrackers[1000]; 219 | global int gFileTrackerCount; 220 | 221 | internal void 222 | TrackAllCodeFiles() 223 | { 224 | WIN32_FIND_DATA Entry; 225 | HANDLE find = FindFirstFile("../code/*.*", &Entry); 226 | if (find != INVALID_HANDLE_VALUE) 227 | { 228 | do 229 | { 230 | if (Entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 231 | { 232 | // directory 233 | } 234 | else 235 | { 236 | size_t FilenameLen = strlen(Entry.cFileName); 237 | if (FilenameLen >= 5) 238 | { 239 | char *Suffix = Entry.cFileName + FilenameLen - 5; 240 | if (strncmp(Suffix, ".hlsl", 5) == 0) 241 | { 242 | if (gFileTrackerCount == ARRAY_COUNT(gFileTrackers)) 243 | { 244 | Win32Panic("Internal Error: File write-time tracker reached maximum count of 1000"); 245 | } 246 | 247 | int CurrTrackerIndex = gFileTrackerCount++; 248 | file_tracker *Tracker = gFileTrackers + CurrTrackerIndex; 249 | 250 | snprintf(Tracker->Path, sizeof(Tracker->Path), 251 | "../code/%s", Entry.cFileName); 252 | Tracker->LastWriteTime = GetLastWriteTime(Tracker->Path); 253 | } 254 | } 255 | } 256 | 257 | } while (FindNextFile(find, &Entry)); 258 | 259 | FindClose(find); 260 | } 261 | } 262 | 263 | int main() 264 | { 265 | SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); 266 | 267 | HMODULE Instance = GetModuleHandleA(0); 268 | 269 | WNDCLASSA WindowClass = {}; 270 | WindowClass.style = CS_OWNDC; 271 | WindowClass.lpfnWndProc = Win32WindowCallback; 272 | WindowClass.hInstance = Instance; 273 | WindowClass.hCursor = LoadCursorA(0, IDC_ARROW); 274 | WindowClass.lpszClassName = "Spectra window class"; 275 | 276 | if (RegisterClassA(&WindowClass)) 277 | { 278 | // first input client rect 279 | RECT WindowRect = {}; 280 | WindowRect.left = 0; 281 | WindowRect.top = 0; 282 | WindowRect.right = gClientWidth; 283 | WindowRect.bottom = gClientHeight; 284 | 285 | DWORD WindowStyle = WS_OVERLAPPEDWINDOW|WS_VISIBLE; 286 | 287 | // adjust to window rect 288 | AdjustWindowRect(&WindowRect, WindowStyle, 0); 289 | int WindowWidth = WindowRect.right - WindowRect.left; 290 | int WindowHeight = WindowRect.bottom - WindowRect.top; 291 | 292 | HWND Window = CreateWindowExA(0, 293 | WindowClass.lpszClassName, 294 | "Spectra", 295 | WindowStyle, 296 | CW_USEDEFAULT, 297 | CW_USEDEFAULT, 298 | WindowWidth, 299 | WindowHeight, 300 | 0, 0, Instance, 0); 301 | 302 | engine Engine = {}; 303 | 304 | TrackAllCodeFiles(); 305 | 306 | LARGE_INTEGER BeginCounter = {}; 307 | LARGE_INTEGER EndCounter = {}; 308 | QueryPerformanceCounter(&BeginCounter); 309 | LARGE_INTEGER CounterFreqency = {}; 310 | QueryPerformanceFrequency(&CounterFreqency); 311 | size_t TicksPerSec = CounterFreqency.QuadPart; 312 | while (!gAppIsDone) 313 | { 314 | TRACKMOUSEEVENT EventToTrack = {}; 315 | EventToTrack.cbSize = sizeof(EventToTrack); 316 | EventToTrack.dwFlags = TME_LEAVE; 317 | EventToTrack.hwndTrack = Window; 318 | EventToTrack.dwHoverTime = HOVER_DEFAULT; 319 | TrackMouseEvent(&EventToTrack); 320 | 321 | gInput.MousedP = {}; 322 | 323 | MSG Message; 324 | while (PeekMessageA(&Message, 0, 0, 0, PM_REMOVE)) 325 | { 326 | TranslateMessage(&Message); 327 | DispatchMessage(&Message); 328 | } 329 | 330 | b32 NeedsReload = false; 331 | for (int TrackerI = 0; TrackerI < gFileTrackerCount; ++TrackerI) 332 | { 333 | file_tracker *Tracker = gFileTrackers + TrackerI; 334 | FILETIME CurrWriteTime = GetLastWriteTime(Tracker->Path); 335 | if (CompareFileTime(&CurrWriteTime, &Tracker->LastWriteTime) != 0) 336 | { 337 | NeedsReload = true; 338 | Tracker->LastWriteTime = CurrWriteTime; 339 | } 340 | } 341 | 342 | QueryPerformanceCounter(&EndCounter); 343 | f32 dT = f32(EndCounter.QuadPart - BeginCounter.QuadPart) / f32(TicksPerSec); 344 | QueryPerformanceCounter(&BeginCounter); 345 | Engine.UpdateAndRender(Window, gClientWidth, gClientHeight, 346 | &gInput, NeedsReload, dT); 347 | 348 | Sleep(1); 349 | } 350 | } 351 | else 352 | { 353 | Win32Panic("Failed to register window class"); 354 | } 355 | 356 | return 0; 357 | } 358 | -------------------------------------------------------------------------------- /code/math.hlsl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "quaternion.hlsl" 4 | 5 | // Approximates luminance from an RGB value 6 | float CalcLuminance(float3 color) 7 | { 8 | return max(dot(color, float3(0.299f, 0.587f, 0.114f)), 0.0001f); 9 | } 10 | 11 | float3 CalcViewP(float3 P, float3 CamP, float4 CamInvQuat) 12 | { 13 | return qrot(P - CamP, CamInvQuat); 14 | } 15 | 16 | float3 TaaTonemap(float3 Col) 17 | { 18 | return Col / (1.0 + CalcLuminance(Col)); 19 | } 20 | 21 | float3 TaaInvTonemap(float3 Col) 22 | { 23 | return Col / (1.0 - CalcLuminance(Col)); 24 | } 25 | -------------------------------------------------------------------------------- /code/note: -------------------------------------------------------------------------------- 1 | 2 | PIPELINING: 3 | 4 | CPU stalled by GPU (single-buffering) 5 | 6 | |CPU| |CPU| 7 | |GPU| |GPU| 8 | 9 | exec time = exec(CPU) + exec(GPU) 10 | 11 | CPU/GPU pipelined (double-buffering) 12 | 13 | |CPU|CPU|CPU|CPU|CPU|CPU| 14 | |GPU|GPU|GPU|GPU|GPU| 15 | 16 | exec time = max(exec(CPU), exec(GPU)) 17 | 18 | RESOURCES: 19 | 20 | persistent resource. no problem 21 | 22 | per-frame resource, create duplicates for each frame 23 | 24 | PERF: 25 | 26 | primary rays: 1.58ms 27 | path tracing: 144.87ms 28 | temporal filter: 0.5ms 29 | calc variance: 5.26ms 30 | spatial filter: 8.76ms 31 | primary shading: 0.29ms 32 | TAA: 0.21ms -------------------------------------------------------------------------------- /code/pack_gbuffer.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), RootConstants(num32BitConstants=8, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D PositionTex: register(u0); 6 | RWTexture2D NormalTex: register(u1); 7 | RWTexture2D GBufferTex: register(u2); 8 | 9 | struct context 10 | { 11 | float3 CamP; 12 | uint Pad1; 13 | 14 | float4 CamInvQuat; 15 | }; 16 | 17 | ConstantBuffer Context: register(b0); 18 | 19 | [RootSignature(RS)] 20 | [numthreads(32, 32, 1)] 21 | void main(uint2 ThreadId: SV_DispatchThreadID) 22 | { 23 | float3 P = PositionTex[ThreadId].xyz; 24 | float3 ViewP = qrot(P - Context.CamP, Context.CamInvQuat); 25 | 26 | float Depth = ViewP.z; 27 | float3 Normal = NormalTex[ThreadId].xyz; 28 | GBufferTex[ThreadId] = float4(Normal, Depth); 29 | } -------------------------------------------------------------------------------- /code/pathtrace.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), DescriptorTable(UAV(u5)), DescriptorTable(UAV(u6)), DescriptorTable(SRV(t0, numDescriptors=64)), RootConstants(num32BitConstants=14, b0)" 2 | 3 | #include "constants.hlsl" 4 | #include "scene.hlsl" 5 | #include "raymarch.hlsl" 6 | #include "random.hlsl" 7 | 8 | RWTexture2D LightTex: register(u0); 9 | RWTexture2D PositionTex: register(u1); 10 | RWTexture2D NormalTex: register(u2); 11 | RWTexture2D AlbedoTex: register(u3); 12 | RWTexture2D EmissionTex: register(u4); 13 | RWTexture2D RayDirTex: register(u5); 14 | RWTexture2D DisocclusionTex: register(u6); 15 | 16 | Texture2D BlueNoiseTexs[64]: register(t0); 17 | 18 | struct context 19 | { 20 | int Width; 21 | int Height; 22 | int FrameIndex; 23 | uint Pad1; 24 | 25 | float3 Ro; 26 | uint Pad2; 27 | float3 At; 28 | float Time; 29 | 30 | float2 PixelOffset; 31 | }; 32 | 33 | ConstantBuffer Context: register(b0); 34 | 35 | float3 SampleHemisphereCosineWeighted(float3 N, float2 R) 36 | { 37 | float3 XAxis; 38 | if (abs(N.y) > 0.99) 39 | { 40 | XAxis = normalize(cross(N, float3(0, 0, 1))); 41 | } 42 | else 43 | { 44 | XAxis = normalize(cross(float3(0, 1, 0), N)); 45 | } 46 | 47 | float3 YAxis = cross(N, XAxis); 48 | 49 | float Radius = sqrt(R.x); 50 | float Angle = R.y * 2.0 * Pi; 51 | 52 | float X = Radius * cos(Angle); 53 | float Y = Radius * sin(Angle); 54 | float Z = sqrt(1.0 - Radius*Radius); 55 | 56 | return X*XAxis + Y*YAxis + Z*N; 57 | } 58 | 59 | float3 SampleConeCosineWeighted(float3 N, float2 R, float Aperture) 60 | { 61 | float3 XAxis; 62 | if (abs(N.y) > 0.99) 63 | { 64 | XAxis = normalize(cross(N, float3(0, 0, 1))); 65 | } 66 | else 67 | { 68 | XAxis = normalize(cross(float3(0, 1, 0), N)); 69 | } 70 | 71 | float3 YAxis = cross(N, XAxis); 72 | 73 | float Radius = sqrt(R.x) * Aperture; 74 | float Angle = R.y * 2.0 * Pi; 75 | 76 | float X = Radius * cos(Angle); 77 | float Y = Radius * sin(Angle); 78 | float Z = sqrt(1.0 - Radius*Radius); 79 | 80 | return X*XAxis + Y*YAxis + Z*N; 81 | } 82 | 83 | [RootSignature(RS)] 84 | [numthreads(32, 32, 1)] 85 | void main(uint2 ThreadId: SV_DispatchThreadID) 86 | { 87 | float2 UV = (float2(ThreadId) + 0.5 + Context.PixelOffset) / float2(Context.Width, Context.Height); 88 | UV = 2.0 * UV - 1.0; 89 | UV.y = -UV.y; 90 | UV.x *= float(Context.Width)/float(Context.Height); 91 | 92 | gSeed = UV * (float(Context.FrameIndex)+1.0); 93 | 94 | bool HasNoHistory = DisocclusionTex[ThreadId] == 1; 95 | 96 | float3 HitP = PositionTex[ThreadId].xyz; 97 | float3 HitN = NormalTex[ThreadId].xyz; 98 | float3 Radiance = 0; 99 | 100 | if (length(HitP) < 10e30) 101 | { 102 | int SampleCount = 1; 103 | if (HasNoHistory) SampleCount *= 8; 104 | float SampleWeight = 1.0 / float(SampleCount); 105 | 106 | float3 SampleAvg = 0; 107 | 108 | for (int SampleI = 0; SampleI < SampleCount; ++SampleI) 109 | { 110 | int BounceCount = 3; 111 | 112 | float3 Sample = 0; 113 | float3 Attenuation = 1.0; 114 | for (int Depth = 0; Depth < BounceCount; ++Depth) 115 | { 116 | float3 SunSampleDir = SampleConeCosineWeighted(SunDir(), Rand2(), SunAperture()); 117 | 118 | if (dot(HitN, SunSampleDir) > 0.0) 119 | { 120 | hit SunHit = RayMarch(HitP + 0.01*HitN, SunSampleDir, Context.Time); 121 | if (SunHit.MatId == -1) 122 | { 123 | float3 SunContrib = dot(HitN, SunSampleDir) * SunLight(); 124 | Sample += Attenuation * SunContrib; 125 | } 126 | } 127 | 128 | float3 Ro = HitP + 0.01*HitN; 129 | float2 R = Rand2(); 130 | float3 Rd = SampleHemisphereCosineWeighted(HitN, R); 131 | 132 | hit Hit = RayMarch(Ro, Rd, Context.Time); 133 | if (Hit.MatId != -1) 134 | { 135 | HitP = Ro + Hit.T*Rd; 136 | HitN = CalcGradient(HitP, Context.Time); 137 | 138 | material Mat = MapMaterial(Hit.MatId, HitP, Context.Time); 139 | 140 | Sample += Attenuation * Mat.Emission; 141 | float3 Brdf = Mat.Albedo; 142 | Attenuation *= Brdf; 143 | } 144 | else 145 | { 146 | Sample += Attenuation * Env(Rd, Context.Time); 147 | break; 148 | } 149 | } 150 | 151 | SampleAvg += SampleWeight * Sample; 152 | 153 | // reload primary surface for next round of sampling 154 | HitP = PositionTex[ThreadId].xyz; 155 | HitN = NormalTex[ThreadId].xyz; 156 | } 157 | 158 | Radiance = SampleAvg; 159 | } 160 | 161 | LightTex[ThreadId] = float4(Radiance, 1.0); 162 | } 163 | -------------------------------------------------------------------------------- /code/primary.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), RootConstants(num32BitConstants=14, b0)" 2 | 3 | #include "constants.hlsl" 4 | #include "scene.hlsl" 5 | #include "raymarch.hlsl" 6 | 7 | RWTexture2D PositionTex: register(u0); 8 | RWTexture2D NormalTex: register(u1); 9 | RWTexture2D AlbedoTex: register(u2); 10 | RWTexture2D EmissionTex: register(u3); 11 | RWTexture2D RayDirTex: register(u4); 12 | 13 | struct context 14 | { 15 | int Width; 16 | int Height; 17 | int FrameIndex; 18 | uint Pad1; 19 | 20 | float3 Ro; 21 | uint Pad2; 22 | float3 At; 23 | float Time; 24 | 25 | float2 PixelOffset; 26 | }; 27 | 28 | ConstantBuffer Context: register(b0); 29 | 30 | [RootSignature(RS)] 31 | [numthreads(32, 32, 1)] 32 | void main(uint2 ThreadId: SV_DispatchThreadID) 33 | { 34 | float2 UV = (float2(ThreadId) + 0.5 + Context.PixelOffset) / float2(Context.Width, Context.Height); 35 | UV = 2.0 * UV - 1.0; 36 | UV.y = -UV.y; 37 | UV.x *= float(Context.Width)/float(Context.Height); 38 | 39 | float3 Ro = Context.Ro; 40 | float3 At = Context.At; 41 | float3 CamZ = normalize(At - Ro); 42 | float3 CamX = normalize(cross(float3(0, 1, 0), CamZ)); 43 | float3 CamY = cross(CamZ, CamX); 44 | float3 Rd = normalize(CamX * UV.x + CamY * UV.y + 1.7 * CamZ); 45 | RayDirTex[ThreadId] = float4(Rd, 0.0); 46 | 47 | hit Hit = RayMarch(Ro, Rd, Context.Time); 48 | float3 HitP = Ro + Hit.T*Rd; 49 | float3 HitN = CalcGradient(HitP, Context.Time); 50 | 51 | if (Hit.MatId != -1) 52 | { 53 | PositionTex[ThreadId] = float4(HitP, 0.0); 54 | NormalTex[ThreadId] = float4(HitN, 0.0); 55 | 56 | material Mat = MapMaterial(Hit.MatId, HitP, Context.Time); 57 | EmissionTex[ThreadId] = float4(Mat.Emission, 1.0); 58 | AlbedoTex[ThreadId] = float4(Mat.Albedo, 1.0); 59 | } 60 | else 61 | { 62 | PositionTex[ThreadId] = 10e31; 63 | NormalTex[ThreadId] = 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /code/quaternion.hlsl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | float4 quat(in float3 axis, in float angle) 4 | { 5 | return float4(axis * sin(angle/2.0), cos(angle/2.0)); 6 | } 7 | 8 | float4 qmul(in float4 q1, in float4 q2) 9 | { 10 | return float4(q1.xyz*q2.w + q2.xyz*q1.w + cross(q1.xyz, q2.xyz), q1.w*q2.w - dot(q1.xyz,q2.xyz)); 11 | } 12 | 13 | float4 qinv(in float4 q) 14 | { 15 | return float4(-q.xyz, q.w); 16 | } 17 | 18 | float3 qrot(in float3 p, in float4 q) 19 | { 20 | return qmul(qmul(q, float4(p, 0.0)), qinv(q)).xyz; 21 | } 22 | -------------------------------------------------------------------------------- /code/random.hlsl: -------------------------------------------------------------------------------- 1 | static float2 gSeed; 2 | 3 | void SeedRand(float InitialSeed) 4 | { 5 | gSeed = InitialSeed; 6 | } 7 | 8 | float2 Rand2() { 9 | gSeed += float2(-1,1); 10 | return float2(frac(sin(dot(gSeed.xy ,float2(12.9898,78.233))) * 43758.5453), 11 | frac(cos(dot(gSeed.xy ,float2(4.898,7.23))) * 23421.631)); 12 | }; 13 | -------------------------------------------------------------------------------- /code/rasterize_text.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT), DescriptorTable(SRV(t0)), StaticSampler(s0, filter=FILTER_MIN_MAG_MIP_LINEAR)" 2 | 3 | Texture2D FontAtlasTex: register(t0); 4 | SamplerState LinearSampler: register(s0); 5 | 6 | struct vs_in 7 | { 8 | float2 P: POS; 9 | float2 UV: TEXCOORD; 10 | int Type: TYPE; 11 | }; 12 | 13 | struct vs_out 14 | { 15 | float4 SV_P: SV_Position; 16 | float2 UV: TEXCOORD; 17 | int Type: TYPE; 18 | }; 19 | 20 | [RootSignature(RS)] 21 | vs_out VS(vs_in In) 22 | { 23 | vs_out Out; 24 | 25 | Out.SV_P = float4(In.P, 0.0, 1.0); 26 | Out.UV = In.UV; 27 | Out.Type = In.Type; 28 | 29 | return Out; 30 | } 31 | 32 | float4 PS(vs_out In): SV_Target 33 | { 34 | if (In.Type == 0) // text 35 | { 36 | float2 ST = In.UV; 37 | float Alpha = FontAtlasTex.Sample(LinearSampler, ST); 38 | return float4(1.0, 1.0, 1.0, Alpha); 39 | } 40 | else // background 41 | { 42 | return float4(0, 0, 0, 0.5); 43 | } 44 | } -------------------------------------------------------------------------------- /code/raymarch.hlsl: -------------------------------------------------------------------------------- 1 | struct hit 2 | { 3 | float T; 4 | int MatId; 5 | }; 6 | 7 | float3 CalcGradient(float3 P, float Time) 8 | { 9 | float2 E = float2(0, 0.0001); 10 | 11 | float3 Gradient; 12 | Gradient.x = Map(P + E.yxx, Time).Dist - Map(P - E.yxx, Time).Dist; 13 | Gradient.y = Map(P + E.xyx, Time).Dist - Map(P - E.xyx, Time).Dist; 14 | Gradient.z = Map(P + E.xxy, Time).Dist - Map(P - E.xxy, Time).Dist; 15 | 16 | return normalize(Gradient); 17 | } 18 | 19 | hit RayMarch(float3 Ro, float3 Rd, float Time) 20 | { 21 | int MatId = -1; 22 | 23 | bool IsRelaxed = true; 24 | float PrevDist = 0; 25 | float PrevStep = 0; 26 | float T = 0.0; 27 | int Iter = 0; 28 | for (Iter = 0; Iter < MAX_RAYMARCH_ITER && T < T_MAX; ++Iter) 29 | { 30 | point_query Q = Map(Ro + T*Rd, Time); 31 | 32 | if (IsRelaxed && abs(Q.Dist) + abs(PrevDist) <= PrevStep) // doesn't intersect 33 | { 34 | IsRelaxed = false; 35 | T -= PrevStep; 36 | Q = Map(Ro + T*Rd, Time); 37 | } 38 | 39 | if (Q.Dist < T_EPSILON) 40 | { 41 | MatId = Q.MatId; 42 | break; 43 | } 44 | 45 | float Scale = IsRelaxed? RAYMARCH_RELAXATION: 1.0; 46 | float Step = Scale * Q.Dist; 47 | T += Step; 48 | 49 | PrevDist = Q.Dist; 50 | PrevStep = Step; 51 | } 52 | 53 | hit Hit; 54 | Hit.T = T; 55 | Hit.MatId = MatId; 56 | return Hit; 57 | } 58 | -------------------------------------------------------------------------------- /code/scene.hlsl: -------------------------------------------------------------------------------- 1 | struct point_query 2 | { 3 | float Dist; 4 | int MatId; 5 | }; 6 | 7 | struct material 8 | { 9 | float3 Albedo; 10 | float3 Emission; 11 | }; 12 | 13 | float2x2 Rotate2(float Theta) 14 | { 15 | float C = cos(Theta); 16 | float S = sin(Theta); 17 | return float2x2(C, -S, S, C); 18 | } 19 | 20 | float sdBox(float3 P, float3 Radius) 21 | { 22 | float3 Q = abs(P) - Radius; 23 | return length(max(Q, 0)) + min(max(max(Q.x, Q.y), Q.z), 0.0); 24 | } 25 | 26 | //point_query Map(float3 P, float Time); 27 | //material MapMaterial(int MatId, float3 P, float Time); 28 | //float3 Env(float3 Rd, float Time); 29 | //float Fog(float Depth); 30 | //float3 SunDir(); 31 | //float3 SunLight(); 32 | //float SunAperture(); 33 | 34 | //#include "demo.hlsl" 35 | #include "tunnel.hlsl" 36 | //#include "sphere2.hlsl" 37 | //#include "cornellbox.hlsl" 38 | //#include "tron.hlsl" 39 | //#include "coolbox.hlsl" 40 | //#include "interior.hlsl" 41 | -------------------------------------------------------------------------------- /code/sdf_engine.cpp: -------------------------------------------------------------------------------- 1 | #include "sdf_engine.h" 2 | 3 | #include "ui.cpp" 4 | 5 | void InitOrResizeUAVTexture2D(ID3D12Device *D, texture *Tex, 6 | int Width, int Height, 7 | DXGI_FORMAT Format, 8 | descriptor_arena *Arena) 9 | { 10 | if (!Tex->Handle) 11 | { 12 | *Tex = InitTexture2D(D, Width, Height, Format, 13 | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, 14 | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); 15 | AssignUAV(D, Tex, Arena); 16 | } 17 | else 18 | { 19 | descriptor PrevUAV = Tex->UAV; 20 | 21 | Tex->Handle->Release(); 22 | *Tex = InitTexture2D(D, Width, Height, Format, 23 | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, 24 | D3D12_RESOURCE_STATE_UNORDERED_ACCESS); 25 | Tex->UAV = PrevUAV; 26 | D->CreateUnorderedAccessView(Tex->Handle, 0, 0, 27 | Tex->UAV.CPUHandle); 28 | } 29 | } 30 | 31 | void 32 | engine::InitOrResizeWindowDependentResources() 33 | { 34 | ID3D12Device *D = Context.Device; 35 | 36 | InitOrResizeUAVTexture2D(D, &NoisyLightTex, Width, Height, 37 | DXGI_FORMAT_R16G16B16A16_FLOAT, 38 | &DescriptorArena); 39 | 40 | InitOrResizeUAVTexture2D(D, &PositionTex, Width, Height, 41 | DXGI_FORMAT_R32G32B32A32_FLOAT, 42 | &DescriptorArena); 43 | 44 | InitOrResizeUAVTexture2D(D, &NormalTex, Width, Height, 45 | DXGI_FORMAT_R16G16B16A16_FLOAT, 46 | &DescriptorArena); 47 | 48 | InitOrResizeUAVTexture2D(D, &AlbedoTex, Width, Height, 49 | DXGI_FORMAT_R16G16B16A16_FLOAT, 50 | &DescriptorArena); 51 | 52 | InitOrResizeUAVTexture2D(D, &EmissionTex, Width, Height, 53 | DXGI_FORMAT_R16G16B16A16_FLOAT, 54 | &DescriptorArena); 55 | 56 | InitOrResizeUAVTexture2D(D, &RayDirTex, Width, Height, 57 | DXGI_FORMAT_R32G32B32A32_FLOAT, 58 | &DescriptorArena); 59 | 60 | InitOrResizeUAVTexture2D(D, &DisocclusionTex, Width, Height, 61 | DXGI_FORMAT_R8_UINT, 62 | &DescriptorArena); 63 | 64 | InitOrResizeUAVTexture2D(D, &PositionHistTex, Width, Height, 65 | DXGI_FORMAT_R32G32B32A32_FLOAT, 66 | &DescriptorArena); 67 | 68 | InitOrResizeUAVTexture2D(D, &NormalHistTex, Width, Height, 69 | DXGI_FORMAT_R16G16B16A16_FLOAT, 70 | &DescriptorArena); 71 | 72 | InitOrResizeUAVTexture2D(D, &LightHistTex, Width, Height, 73 | DXGI_FORMAT_R16G16B16A16_FLOAT, 74 | &DescriptorArena); 75 | 76 | InitOrResizeUAVTexture2D(D, &LumMomentTex, Width, Height, 77 | DXGI_FORMAT_R16G16_FLOAT, 78 | &DescriptorArena); 79 | 80 | InitOrResizeUAVTexture2D(D, &LumMomentHistTex, Width, Height, 81 | DXGI_FORMAT_R16G16_FLOAT, 82 | &DescriptorArena); 83 | 84 | InitOrResizeUAVTexture2D(D, &VarianceTex, Width, Height, 85 | DXGI_FORMAT_R32_FLOAT, 86 | &DescriptorArena); 87 | 88 | InitOrResizeUAVTexture2D(D, &NextVarianceTex, Width, Height, 89 | DXGI_FORMAT_R32_FLOAT, 90 | &DescriptorArena); 91 | 92 | InitOrResizeUAVTexture2D(D, &GBufferTex, Width, Height, 93 | DXGI_FORMAT_R32G32B32A32_FLOAT, 94 | &DescriptorArena); 95 | 96 | InitOrResizeUAVTexture2D(D, &PrevPixelIdTex, Width, Height, 97 | DXGI_FORMAT_R32G32_FLOAT, 98 | &DescriptorArena); 99 | 100 | InitOrResizeUAVTexture2D(D, &IntegratedLightTex, Width, Height, 101 | DXGI_FORMAT_R16G16B16A16_FLOAT, 102 | &DescriptorArena); 103 | 104 | InitOrResizeUAVTexture2D(D, &TempTex, Width, Height, 105 | DXGI_FORMAT_R16G16B16A16_FLOAT, 106 | &DescriptorArena); 107 | 108 | InitOrResizeUAVTexture2D(D, &TaaOutputTex, Width, Height, 109 | DXGI_FORMAT_R16G16B16A16_FLOAT, 110 | &DescriptorArena); 111 | 112 | InitOrResizeUAVTexture2D(D, &TaaHistTex, Width, Height, 113 | DXGI_FORMAT_R16G16B16A16_FLOAT, 114 | &DescriptorArena); 115 | 116 | InitOrResizeUAVTexture2D(D, &OutputTex, Width, Height, 117 | DXGI_FORMAT_R8G8B8A8_UNORM, 118 | &DescriptorArena); 119 | } 120 | 121 | internal b32 122 | VerifyComputeShader(char *Filename, char *EntryPoint, ID3DBlob **ErrorBlob_Out) 123 | { 124 | file File = ReadTextFile(Filename); 125 | if (!File.Data) 126 | { 127 | Win32MessageBox("Shader Load Failure", MB_OK|MB_ICONERROR, 128 | "Failed to load shader %s", Filename); 129 | return false; 130 | } 131 | 132 | b32 HasError = 0; 133 | ID3DBlob *Blob = CompileShader(File, Filename, EntryPoint, "cs_5_1", &HasError); 134 | free(File.Data); 135 | 136 | if (HasError) 137 | { 138 | *ErrorBlob_Out = Blob; 139 | return false; 140 | } 141 | 142 | Blob->Release(); 143 | return true; 144 | } 145 | 146 | void 147 | frame_data::BeginProfile(ID3D12GraphicsCommandList *CmdList, char *Identifer) 148 | { 149 | ASSERT(!IsInSession); 150 | IsInSession = true; 151 | 152 | int Index = CurrQueryIndex++; 153 | CmdList->EndQuery(QueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, Index); 154 | 155 | Sessions[CurrSessionIndex].Name = strdup(Identifer); 156 | Sessions[CurrSessionIndex].BeginIndex = Index; 157 | } 158 | 159 | void 160 | frame_data::EndProfile(ID3D12GraphicsCommandList *CmdList) 161 | { 162 | ASSERT(IsInSession); 163 | 164 | int Index = CurrQueryIndex++; 165 | CmdList->EndQuery(QueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, Index); 166 | 167 | Sessions[CurrSessionIndex].EndIndex = Index; 168 | CurrSessionIndex += 1; 169 | 170 | IsInSession = false; 171 | } 172 | 173 | void 174 | frame_data::ReadbackCounters() 175 | { 176 | if (CurrQueryIndex == 0) return; 177 | 178 | D3D12_RANGE ReadRange = {0, sizeof(u64) * CurrQueryIndex}; 179 | 180 | u64 *Counters = 0; 181 | DXOP(CounterBuffer->Map(0, &ReadRange, (void **)&Counters)); 182 | 183 | D3D12_RANGE WriteRange = {}; 184 | CounterBuffer->Unmap(0, &WriteRange); 185 | 186 | // record results 187 | printf("PROFILE RESULTS ------------------------------\n"); 188 | for (int SessionI = 0; SessionI < CurrSessionIndex; ++SessionI) 189 | { 190 | profile_session *Session = Sessions + SessionI; 191 | 192 | u64 BeginCounter = Counters[Session->BeginIndex]; 193 | u64 EndCounter = Counters[Session->EndIndex]; 194 | f32 Miliseconds = 1000.0f * f32(EndCounter - BeginCounter) / f32(TimestampFrequency); 195 | 196 | printf("%s: %.2fms\n", Session->Name, Miliseconds); 197 | } 198 | printf("----------------------------------------------\n"); 199 | 200 | for (int SessionI = 0; SessionI < CurrSessionIndex; ++SessionI) 201 | { 202 | profile_session *Session = Sessions + SessionI; 203 | free(Session->Name); 204 | } 205 | CurrQueryIndex = 0; 206 | CurrSessionIndex = 0; 207 | } 208 | 209 | void 210 | engine::UpdateAndRender(HWND Window, int ClientWidth, int ClientHeight, 211 | input *Input, b32 NeedsReload, f32 dT) 212 | { 213 | //NOTE(chen): if window is minimized, don't run at all 214 | if (ClientWidth == 0 || ClientHeight == 0) return; 215 | 216 | if (!IsInitialized) 217 | { 218 | ID3D12Debug *Debug = 0; 219 | DXOP(D3D12GetDebugInterface(IID_PPV_ARGS(&Debug))); 220 | Debug->EnableDebugLayer(); 221 | 222 | ID3D12Device *D = 0; 223 | DXOP(D3D12CreateDevice(0, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&D))); 224 | 225 | Context = InitGPUContext(D, BACKBUFFER_COUNT); 226 | 227 | SwapChain = CreateSwapChain(Context.CmdQueue, Window, BACKBUFFER_COUNT); 228 | Width = ClientWidth; 229 | Height = ClientHeight; 230 | 231 | DescriptorArena = InitDescriptorArena(D, 100, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); 232 | RTVArena = InitDescriptorArena(D, 100, D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 233 | 234 | for (int I = 0; I < BACKBUFFER_COUNT; ++I) 235 | { 236 | ID3D12Resource *BackBuffer = 0; 237 | SwapChain->GetBuffer(I, IID_PPV_ARGS(&BackBuffer)); 238 | BackBufferTexs[I] = WrapTexture(BackBuffer, D3D12_RESOURCE_STATE_PRESENT); 239 | AssignRTV(D, &BackBufferTexs[I], &RTVArena); 240 | } 241 | 242 | for (int FrameI = 0; FrameI < ARRAY_COUNT(FrameData); ++FrameI) 243 | { 244 | frame_data *Frame = FrameData + FrameI; 245 | DXOP(Context.CmdQueue->GetTimestampFrequency(&Frame->TimestampFrequency)); 246 | Frame->QueryCount = 1000; 247 | 248 | D3D12_QUERY_HEAP_DESC QueryHeapDesc = {}; 249 | QueryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; 250 | QueryHeapDesc.Count = Frame->QueryCount; 251 | DXOP(D->CreateQueryHeap(&QueryHeapDesc, IID_PPV_ARGS(&Frame->QueryHeap))); 252 | 253 | Frame->CounterBuffer = InitBuffer(D, sizeof(u64) * Frame->QueryCount, D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_FLAG_NONE); 254 | } 255 | 256 | UISystem = InitUISystem(&Context, &DescriptorArena); 257 | 258 | PrimaryPSO = InitComputePSO(D, "../code/primary.hlsl", "main"); 259 | PathTracePSO = InitComputePSO(D, "../code/pathtrace.hlsl", "main"); 260 | ComputeDisocclusionPSO = InitComputePSO(D, "../code/compute_disocclusion.hlsl", "main"); 261 | TemporalFilterPSO = InitComputePSO(D, "../code/temporal_filter.hlsl", "main"); 262 | CalcVariancePSO = InitComputePSO(D, "../code/calc_variance.hlsl", "main"); 263 | PackGBufferPSO = InitComputePSO(D, "../code/pack_gbuffer.hlsl", "main"); 264 | CorrelateHistoryPSO = InitComputePSO(D, "../code/correlate_history.hlsl", "main"); 265 | SpatialFilterPSO = InitComputePSO(D, "../code/spatial_filter.hlsl", "main"); 266 | ApplyPrimaryShadingPSO = InitComputePSO(D, "../code/apply_primary_shading.hlsl", "main"); 267 | TaaPSO = InitComputePSO(D, "../code/taa.hlsl", "main"); 268 | ToneMapPSO = InitComputePSO(D, "../code/tonemap.hlsl", "main"); 269 | 270 | for (int I = 0; I < 64; ++I) 271 | { 272 | char BlueNoiseFilename[256]; 273 | snprintf(BlueNoiseFilename, sizeof(BlueNoiseFilename), 274 | "../data/textures/HDR_RGBA_%d.png", I); 275 | BlueNoiseTexs[I] = Context.LoadTexture2D(BlueNoiseFilename, 276 | DXGI_FORMAT_R8G8B8A8_UNORM, 277 | D3D12_RESOURCE_FLAG_NONE, 278 | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); 279 | AssignSRV(D, BlueNoiseTexs+I, &DescriptorArena); 280 | } 281 | 282 | InitOrResizeWindowDependentResources(); 283 | 284 | Camera.P = {0.0f, 0.0f, -4.0f}; 285 | Camera.Orientation = Quaternion(); 286 | 287 | for (u64 I = 0; I < ARRAY_COUNT(HaltonSequence); ++I) 288 | { 289 | HaltonSequence[I].X = RadicalInverse(I+1, 2); 290 | HaltonSequence[I].Y = RadicalInverse(I+1, 3); 291 | } 292 | 293 | IsInitialized = true; 294 | } 295 | Time += dT; 296 | 297 | if (ClientWidth != Width || ClientHeight != Height) 298 | { 299 | Context.FlushFramesInFlight(); 300 | 301 | Width = ClientWidth; 302 | Height = ClientHeight; 303 | 304 | UISystem.BuildUIVB(&Context, Width, Height); 305 | InitOrResizeWindowDependentResources(); 306 | 307 | for (int BI = 0; BI < BACKBUFFER_COUNT; ++BI) 308 | { 309 | BackBufferTexs[BI].Handle->Release(); 310 | } 311 | 312 | DXOP(SwapChain->ResizeBuffers(BACKBUFFER_COUNT, 0, 0, 313 | DXGI_FORMAT_R8G8B8A8_UNORM, 0)); 314 | 315 | for (int BI = 0; BI < BACKBUFFER_COUNT; ++BI) 316 | { 317 | ID3D12Resource *BackBuffer = 0; 318 | SwapChain->GetBuffer(BI, IID_PPV_ARGS(&BackBuffer)); 319 | 320 | descriptor PrevRTV = BackBufferTexs[BI].RTV; 321 | BackBufferTexs[BI] = WrapTexture(BackBuffer, D3D12_RESOURCE_STATE_PRESENT); 322 | BackBufferTexs[BI].RTV = PrevRTV; 323 | Context.Device->CreateRenderTargetView(BackBufferTexs[BI].Handle, 324 | 0, BackBufferTexs[BI].RTV.CPUHandle); 325 | } 326 | } 327 | 328 | if (NeedsReload) 329 | { 330 | Context.FlushFramesInFlight(); 331 | 332 | ID3D12Device *D = Context.Device; 333 | 334 | b32 ShadersAreValid = true; 335 | ID3DBlob *ErrorBlob = 0; 336 | if (ShadersAreValid) ShadersAreValid = VerifyComputeShader("../code/primary.hlsl", "main", &ErrorBlob); 337 | if (ShadersAreValid) ShadersAreValid = VerifyComputeShader("../code/pathtrace.hlsl", "main", &ErrorBlob); 338 | if (ShadersAreValid) ShadersAreValid = VerifyComputeShader("../code/apply_primary_shading.hlsl", "main", &ErrorBlob); 339 | 340 | if (ErrorBlob) 341 | { 342 | UISystem.SetErrorMessage((char *)ErrorBlob->GetBufferPointer()); 343 | ErrorBlob->Release(); 344 | UISystem.BuildUIVB(&Context, Width, Height); 345 | } 346 | else 347 | { 348 | UISystem.SetErrorMessage(""); 349 | UISystem.BuildUIVB(&Context, Width, Height); 350 | } 351 | 352 | if (ShadersAreValid) 353 | { 354 | PrimaryPSO.Release(); 355 | PathTracePSO.Release(); 356 | ApplyPrimaryShadingPSO.Release(); 357 | 358 | PrimaryPSO = InitComputePSO(D, "../code/primary.hlsl", "main"); 359 | PathTracePSO = InitComputePSO(D, "../code/pathtrace.hlsl", "main"); 360 | ApplyPrimaryShadingPSO = InitComputePSO(D, "../code/apply_primary_shading.hlsl", "main"); 361 | } 362 | 363 | FrameIndex = 0; 364 | } 365 | 366 | // camera orientation 367 | if (Input->MouseDown) 368 | { 369 | v2 MousedP = {f32(Input->MousedP.X) / f32(Width), f32(Input->MousedP.Y) / f32(Height)}; 370 | quaternion XRot = Quaternion(YAxis(), MousedP.X * Pi32); 371 | Camera.Orientation = XRot * Camera.Orientation; 372 | 373 | v3 LocalXAxis = Rotate(XAxis(), Camera.Orientation); 374 | quaternion YRot = Quaternion(LocalXAxis, MousedP.Y * Pi32); 375 | Camera.Orientation = YRot * Camera.Orientation; 376 | } 377 | 378 | // camera translation 379 | v3 dP = {}; 380 | if (Input->Keys['W']) dP.Z += 1.0f; 381 | if (Input->Keys['S']) dP.Z -= 1.0f; 382 | if (Input->Keys['A']) dP.X -= 1.0f; 383 | if (Input->Keys['D']) dP.X += 1.0f; 384 | dP = Normalize(dP); 385 | dP = Rotate(dP, Camera.Orientation); 386 | f32 Speed = 5.0f; 387 | Camera.P += Speed * dP * dT; 388 | 389 | UINT CurrBackbufferIndex = SwapChain->GetCurrentBackBufferIndex(); 390 | frame_data *Frame = FrameData + CurrBackbufferIndex; 391 | 392 | Context.Reset(CurrBackbufferIndex); 393 | ID3D12GraphicsCommandList *CmdList = Context.CmdList; 394 | ID3D12CommandQueue *CmdQueue = Context.CmdQueue; 395 | 396 | int ThreadGroupCountX = (Width-1)/32 + 1; 397 | int ThreadGroupCountY = (Height-1)/32 + 1; 398 | 399 | v3 CamOffset = Rotate(ZAxis(), Camera.Orientation); 400 | v3 CamAt = Camera.P + CamOffset; 401 | v2 PixelOffset = V2(-0.5f) + HaltonSequence[FrameIndex % ARRAY_COUNT(HaltonSequence)]; 402 | 403 | if (Input->Keys['N']) SwitchViewThreshold -= 10; 404 | if (Input->Keys['M']) SwitchViewThreshold += 10; 405 | if (SwitchViewThreshold < 0) SwitchViewThreshold = 0; 406 | if (SwitchViewThreshold >= Width) SwitchViewThreshold = Width-1; 407 | 408 | // primary rays 409 | Frame->BeginProfile(CmdList, "Primary rays"); 410 | { 411 | CmdList->SetPipelineState(PrimaryPSO.Handle); 412 | CmdList->SetComputeRootSignature(PrimaryPSO.RootSignature); 413 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 414 | CmdList->SetComputeRootDescriptorTable(0, PositionTex.UAV.GPUHandle); 415 | CmdList->SetComputeRootDescriptorTable(1, NormalTex.UAV.GPUHandle); 416 | CmdList->SetComputeRootDescriptorTable(2, AlbedoTex.UAV.GPUHandle); 417 | CmdList->SetComputeRootDescriptorTable(3, EmissionTex.UAV.GPUHandle); 418 | CmdList->SetComputeRootDescriptorTable(4, RayDirTex.UAV.GPUHandle); 419 | CmdList->SetComputeRoot32BitConstants(5, 1, &Width, 0); 420 | CmdList->SetComputeRoot32BitConstants(5, 1, &Height, 1); 421 | CmdList->SetComputeRoot32BitConstants(5, 1, &FrameIndex, 2); 422 | CmdList->SetComputeRoot32BitConstants(5, 3, &Camera.P, 4); 423 | CmdList->SetComputeRoot32BitConstants(5, 3, &CamAt, 8); 424 | CmdList->SetComputeRoot32BitConstants(5, 1, &Time, 11); 425 | CmdList->SetComputeRoot32BitConstants(5, 2, &PixelOffset, 12); 426 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 427 | 428 | Context.UAVBarrier(&PositionTex); 429 | Context.UAVBarrier(&NormalTex); 430 | Context.UAVBarrier(&AlbedoTex); 431 | Context.UAVBarrier(&EmissionTex); 432 | Context.UAVBarrier(&RayDirTex); 433 | Context.FlushBarriers(); 434 | } 435 | Frame->EndProfile(CmdList); 436 | 437 | // correlate history pixels 438 | Frame->BeginProfile(CmdList, "Correlate history pixels"); 439 | { 440 | quaternion PrevCameraInvOrientation = Conjugate(PrevCamera.Orientation); 441 | 442 | CmdList->SetPipelineState(CorrelateHistoryPSO.Handle); 443 | CmdList->SetComputeRootSignature(CorrelateHistoryPSO.RootSignature); 444 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 445 | CmdList->SetComputeRootDescriptorTable(0, PositionTex.UAV.GPUHandle); 446 | CmdList->SetComputeRootDescriptorTable(1, PrevPixelIdTex.UAV.GPUHandle); 447 | CmdList->SetComputeRoot32BitConstants(2, 3, &PrevCamera.P, 0); 448 | CmdList->SetComputeRoot32BitConstants(2, 4, &PrevCameraInvOrientation, 4); 449 | CmdList->SetComputeRoot32BitConstants(2, 1, &Width, 8); 450 | CmdList->SetComputeRoot32BitConstants(2, 1, &Height, 9); 451 | CmdList->SetComputeRoot32BitConstants(2, 1, &FrameIndex, 10); 452 | CmdList->SetComputeRoot32BitConstants(2, 2, &PixelOffset, 12); 453 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 454 | 455 | Context.UAVBarrier(&PrevPixelIdTex); 456 | Context.FlushBarriers(); 457 | } 458 | Frame->EndProfile(CmdList); 459 | 460 | // compute disocclusion 461 | Frame->BeginProfile(CmdList, "Compute disocclusion"); 462 | { 463 | CmdList->SetPipelineState(ComputeDisocclusionPSO.Handle); 464 | CmdList->SetComputeRootSignature(ComputeDisocclusionPSO.RootSignature); 465 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 466 | CmdList->SetComputeRootDescriptorTable(0, PositionTex.UAV.GPUHandle); 467 | CmdList->SetComputeRootDescriptorTable(1, NormalTex.UAV.GPUHandle); 468 | CmdList->SetComputeRootDescriptorTable(2, PositionHistTex.UAV.GPUHandle); 469 | CmdList->SetComputeRootDescriptorTable(3, NormalHistTex.UAV.GPUHandle); 470 | CmdList->SetComputeRootDescriptorTable(4, PrevPixelIdTex.UAV.GPUHandle); 471 | CmdList->SetComputeRootDescriptorTable(5, DisocclusionTex.UAV.GPUHandle); 472 | CmdList->SetComputeRoot32BitConstants(6, 1, &FrameIndex, 0); 473 | CmdList->SetComputeRoot32BitConstants(6, 3, &Camera.P, 1); 474 | CmdList->SetComputeRoot32BitConstants(6, 1, &Width, 4); 475 | CmdList->SetComputeRoot32BitConstants(6, 1, &Height, 5); 476 | 477 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 478 | 479 | Context.UAVBarrier(&DisocclusionTex); 480 | Context.FlushBarriers(); 481 | } 482 | Frame->EndProfile(CmdList); 483 | 484 | // path tracing 485 | Frame->BeginProfile(CmdList, "Path tracing"); 486 | { 487 | CmdList->SetPipelineState(PathTracePSO.Handle); 488 | CmdList->SetComputeRootSignature(PathTracePSO.RootSignature); 489 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 490 | CmdList->SetComputeRootDescriptorTable(0, NoisyLightTex.UAV.GPUHandle); 491 | CmdList->SetComputeRootDescriptorTable(1, PositionTex.UAV.GPUHandle); 492 | CmdList->SetComputeRootDescriptorTable(2, NormalTex.UAV.GPUHandle); 493 | CmdList->SetComputeRootDescriptorTable(3, AlbedoTex.UAV.GPUHandle); 494 | CmdList->SetComputeRootDescriptorTable(4, EmissionTex.UAV.GPUHandle); 495 | CmdList->SetComputeRootDescriptorTable(5, RayDirTex.UAV.GPUHandle); 496 | CmdList->SetComputeRootDescriptorTable(6, DisocclusionTex.UAV.GPUHandle); 497 | CmdList->SetComputeRootDescriptorTable(7, BlueNoiseTexs[0].SRV.GPUHandle); 498 | CmdList->SetComputeRoot32BitConstants(8, 1, &Width, 0); 499 | CmdList->SetComputeRoot32BitConstants(8, 1, &Height, 1); 500 | CmdList->SetComputeRoot32BitConstants(8, 1, &FrameIndex, 2); 501 | CmdList->SetComputeRoot32BitConstants(8, 3, &Camera.P, 4); 502 | CmdList->SetComputeRoot32BitConstants(8, 3, &CamAt, 8); 503 | CmdList->SetComputeRoot32BitConstants(8, 1, &Time, 11); 504 | CmdList->SetComputeRoot32BitConstants(8, 2, &PixelOffset, 12); 505 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 506 | 507 | Context.UAVBarrier(&NoisyLightTex); 508 | Context.UAVBarrier(&PositionTex); 509 | Context.UAVBarrier(&NormalTex); 510 | Context.UAVBarrier(&AlbedoTex); 511 | Context.UAVBarrier(&EmissionTex); 512 | Context.UAVBarrier(&RayDirTex); 513 | Context.FlushBarriers(); 514 | } 515 | Frame->EndProfile(CmdList); 516 | 517 | // pack gbuffer 518 | Frame->BeginProfile(CmdList, "Pack gbuffer"); 519 | { 520 | quaternion CameraInvOrientation = Conjugate(Camera.Orientation); 521 | 522 | CmdList->SetPipelineState(PackGBufferPSO.Handle); 523 | CmdList->SetComputeRootSignature(PackGBufferPSO.RootSignature); 524 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 525 | CmdList->SetComputeRootDescriptorTable(0, PositionTex.UAV.GPUHandle); 526 | CmdList->SetComputeRootDescriptorTable(1, NormalTex.UAV.GPUHandle); 527 | CmdList->SetComputeRootDescriptorTable(2, GBufferTex.UAV.GPUHandle); 528 | CmdList->SetComputeRoot32BitConstants(3, 3, &Camera.P, 0); 529 | CmdList->SetComputeRoot32BitConstants(3, 4, &CameraInvOrientation, 4); 530 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 531 | 532 | Context.UAVBarrier(&GBufferTex); 533 | Context.FlushBarriers(); 534 | } 535 | Frame->EndProfile(CmdList); 536 | 537 | // temporal filter 538 | Frame->BeginProfile(CmdList, "Temporal Filter"); 539 | { 540 | quaternion CurrCameraInvOrientation = Conjugate(Camera.Orientation); 541 | 542 | CmdList->SetPipelineState(TemporalFilterPSO.Handle); 543 | CmdList->SetComputeRootSignature(TemporalFilterPSO.RootSignature); 544 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 545 | CmdList->SetComputeRootDescriptorTable(0, NoisyLightTex.UAV.GPUHandle); 546 | CmdList->SetComputeRootDescriptorTable(1, LightHistTex.UAV.GPUHandle); 547 | CmdList->SetComputeRootDescriptorTable(2, IntegratedLightTex.UAV.GPUHandle); 548 | CmdList->SetComputeRootDescriptorTable(3, PositionTex.UAV.GPUHandle); 549 | CmdList->SetComputeRootDescriptorTable(4, NormalTex.UAV.GPUHandle); 550 | CmdList->SetComputeRootDescriptorTable(5, PositionHistTex.UAV.GPUHandle); 551 | CmdList->SetComputeRootDescriptorTable(6, NormalHistTex.UAV.GPUHandle); 552 | CmdList->SetComputeRootDescriptorTable(7, LumMomentHistTex.UAV.GPUHandle); 553 | CmdList->SetComputeRootDescriptorTable(8, LumMomentTex.UAV.GPUHandle); 554 | CmdList->SetComputeRootDescriptorTable(9, PrevPixelIdTex.UAV.GPUHandle); 555 | CmdList->SetComputeRoot32BitConstants(10, 1, &FrameIndex, 0); 556 | CmdList->SetComputeRoot32BitConstants(10, 3, &Camera.P, 1); 557 | CmdList->SetComputeRoot32BitConstants(10, 4, &CurrCameraInvOrientation, 4); 558 | CmdList->SetComputeRoot32BitConstants(10, 1, &Width, 8); 559 | CmdList->SetComputeRoot32BitConstants(10, 1, &Height, 9); 560 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 561 | 562 | Context.UAVBarrier(&IntegratedLightTex); 563 | Context.UAVBarrier(&LumMomentTex); 564 | Context.FlushBarriers(); 565 | 566 | Context.CopyResourceBarriered(&LightHistTex, &IntegratedLightTex); 567 | Context.CopyResourceBarriered(&LumMomentHistTex, &LumMomentTex); 568 | Context.CopyResourceBarriered(&PositionHistTex, &PositionTex); 569 | Context.CopyResourceBarriered(&NormalHistTex, &NormalTex); 570 | } 571 | Frame->EndProfile(CmdList); 572 | 573 | //NOTE(chen): record temporal filtered only result to help debug spatial filter 574 | { 575 | Context.CopyResourceBarriered(&NoisyLightTex, &IntegratedLightTex); 576 | } 577 | 578 | // calc variance 579 | Frame->BeginProfile(CmdList, "Calc variance"); 580 | { 581 | CmdList->SetPipelineState(CalcVariancePSO.Handle); 582 | CmdList->SetComputeRootSignature(CalcVariancePSO.RootSignature); 583 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 584 | CmdList->SetComputeRootDescriptorTable(0, LumMomentTex.UAV.GPUHandle); 585 | CmdList->SetComputeRootDescriptorTable(1, VarianceTex.UAV.GPUHandle); 586 | CmdList->SetComputeRootDescriptorTable(2, IntegratedLightTex.UAV.GPUHandle); 587 | CmdList->SetComputeRootDescriptorTable(3, GBufferTex.UAV.GPUHandle); 588 | CmdList->SetComputeRoot32BitConstants(4, 3, &Camera.P, 0); 589 | 590 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 591 | 592 | Context.UAVBarrier(&VarianceTex); 593 | Context.FlushBarriers(); 594 | } 595 | Frame->EndProfile(CmdList); 596 | 597 | // spatial filter 598 | Frame->BeginProfile(CmdList, "Spatial filter"); 599 | { 600 | int Iterations = 6; 601 | // this gaurantees the end result ends up in IntegratedNoisyLightTex 602 | ASSERT(Iterations % 2 == 0); 603 | 604 | texture *PingPongs[2] = {&IntegratedLightTex, &TempTex}; 605 | texture *Variances[2] = {&VarianceTex, &NextVarianceTex}; 606 | 607 | for (int Depth = 0; Depth < Iterations; ++Depth) 608 | { 609 | int FilterStride = 1 << Depth; 610 | 611 | CmdList->SetPipelineState(SpatialFilterPSO.Handle); 612 | CmdList->SetComputeRootSignature(SpatialFilterPSO.RootSignature); 613 | CmdList->SetDescriptorHeaps(1, &DescriptorArena.Heap); 614 | CmdList->SetComputeRootDescriptorTable(0, PingPongs[0]->UAV.GPUHandle); 615 | CmdList->SetComputeRootDescriptorTable(1, PingPongs[1]->UAV.GPUHandle); 616 | CmdList->SetComputeRootDescriptorTable(2, GBufferTex.UAV.GPUHandle); 617 | CmdList->SetComputeRootDescriptorTable(3, Variances[0]->UAV.GPUHandle); 618 | CmdList->SetComputeRootDescriptorTable(4, Variances[1]->UAV.GPUHandle); 619 | CmdList->SetComputeRootDescriptorTable(5, LightHistTex.UAV.GPUHandle); 620 | CmdList->SetComputeRoot32BitConstants(6, 3, &Camera.P, 0); 621 | CmdList->SetComputeRoot32BitConstants(6, 1, &Depth, 3); 622 | CmdList->SetComputeRoot32BitConstants(6, 1, &FilterStride, 4); 623 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 624 | 625 | Context.UAVBarrier(PingPongs[1]); 626 | Context.UAVBarrier(Variances[1]); 627 | Context.FlushBarriers(); 628 | 629 | // swap 630 | texture *Temp = PingPongs[1]; 631 | PingPongs[1] = PingPongs[0]; 632 | PingPongs[0] = Temp; 633 | 634 | // swap 635 | texture *VarTemp = Variances[1]; 636 | Variances[1] = Variances[0]; 637 | Variances[0] = VarTemp; 638 | } 639 | } 640 | Frame->EndProfile(CmdList); 641 | 642 | // apply primary shading 643 | Frame->BeginProfile(CmdList, "Primary shading"); 644 | { 645 | CmdList->SetPipelineState(ApplyPrimaryShadingPSO.Handle); 646 | CmdList->SetComputeRootSignature(ApplyPrimaryShadingPSO.RootSignature); 647 | CmdList->SetComputeRootDescriptorTable(0, IntegratedLightTex.UAV.GPUHandle); 648 | CmdList->SetComputeRootDescriptorTable(1, PositionTex.UAV.GPUHandle); 649 | CmdList->SetComputeRootDescriptorTable(2, AlbedoTex.UAV.GPUHandle); 650 | CmdList->SetComputeRootDescriptorTable(3, EmissionTex.UAV.GPUHandle); 651 | CmdList->SetComputeRootDescriptorTable(4, RayDirTex.UAV.GPUHandle); 652 | CmdList->SetComputeRoot32BitConstants(5, 1, &Time, 0); 653 | CmdList->SetComputeRoot32BitConstants(5, 3, &Camera.P, 1); 654 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 655 | 656 | Context.UAVBarrier(&IntegratedLightTex); 657 | Context.FlushBarriers(); 658 | } 659 | Frame->EndProfile(CmdList); 660 | 661 | // apply primary shading on noisy input for comparison 662 | { 663 | CmdList->SetPipelineState(ApplyPrimaryShadingPSO.Handle); 664 | CmdList->SetComputeRootSignature(ApplyPrimaryShadingPSO.RootSignature); 665 | CmdList->SetComputeRootDescriptorTable(0, NoisyLightTex.UAV.GPUHandle); 666 | CmdList->SetComputeRootDescriptorTable(1, PositionTex.UAV.GPUHandle); 667 | CmdList->SetComputeRootDescriptorTable(2, AlbedoTex.UAV.GPUHandle); 668 | CmdList->SetComputeRootDescriptorTable(3, EmissionTex.UAV.GPUHandle); 669 | CmdList->SetComputeRootDescriptorTable(4, RayDirTex.UAV.GPUHandle); 670 | CmdList->SetComputeRoot32BitConstants(5, 1, &Time, 0); 671 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 672 | 673 | Context.UAVBarrier(&IntegratedLightTex); 674 | Context.FlushBarriers(); 675 | } 676 | 677 | // TAA 678 | Frame->BeginProfile(CmdList, "TAA"); 679 | { 680 | CmdList->SetPipelineState(TaaPSO.Handle); 681 | CmdList->SetComputeRootSignature(TaaPSO.RootSignature); 682 | CmdList->SetComputeRootDescriptorTable(0, IntegratedLightTex.UAV.GPUHandle); 683 | CmdList->SetComputeRootDescriptorTable(1, TaaHistTex.UAV.GPUHandle); 684 | CmdList->SetComputeRootDescriptorTable(2, TaaOutputTex.UAV.GPUHandle); 685 | CmdList->SetComputeRootDescriptorTable(3, PrevPixelIdTex.UAV.GPUHandle); 686 | CmdList->SetComputeRoot32BitConstants(4, 1, &FrameIndex, 0); 687 | CmdList->SetComputeRoot32BitConstants(4, 1, &Width, 1); 688 | CmdList->SetComputeRoot32BitConstants(4, 1, &Height, 2); 689 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 690 | 691 | Context.UAVBarrier(&TaaOutputTex); 692 | Context.FlushBarriers(); 693 | 694 | Context.CopyResourceBarriered(&TaaHistTex, &TaaOutputTex); 695 | } 696 | Frame->EndProfile(CmdList); 697 | 698 | Context.UAVBarrier(&LightHistTex); 699 | Context.FlushBarriers(); 700 | 701 | // tonemap 702 | { 703 | CmdList->SetPipelineState(ToneMapPSO.Handle); 704 | CmdList->SetComputeRootSignature(ToneMapPSO.RootSignature); 705 | CmdList->SetComputeRootDescriptorTable(0, TaaOutputTex.UAV.GPUHandle); 706 | CmdList->SetComputeRootDescriptorTable(1, OutputTex.UAV.GPUHandle); 707 | CmdList->SetComputeRootDescriptorTable(2, NoisyLightTex.UAV.GPUHandle); 708 | CmdList->SetComputeRootDescriptorTable(3, BlueNoiseTexs[0].SRV.GPUHandle); 709 | CmdList->SetComputeRoot32BitConstants(4, 1, &SwitchViewThreshold, 0); 710 | CmdList->SetComputeRoot32BitConstants(4, 1, &FrameIndex, 1); 711 | CmdList->Dispatch(ThreadGroupCountX, ThreadGroupCountY, 1); 712 | 713 | Context.UAVBarrier(&OutputTex); 714 | Context.FlushBarriers(); 715 | } 716 | 717 | UINT BackBufferIndex = SwapChain->GetCurrentBackBufferIndex(); 718 | texture *BackBufferTex = BackBufferTexs + BackBufferIndex; 719 | Context.CopyResourceBarriered(BackBufferTex, &OutputTex); 720 | 721 | Context.TransitionBarrier(BackBufferTex, D3D12_RESOURCE_STATE_RENDER_TARGET); 722 | Context.FlushBarriers(); 723 | 724 | UISystem.DrawMessage(&Context, *BackBufferTex); 725 | 726 | Context.TransitionBarrier(BackBufferTex, D3D12_RESOURCE_STATE_PRESENT); 727 | Context.FlushBarriers(); 728 | 729 | CmdList->ResolveQueryData(Frame->QueryHeap, D3D12_QUERY_TYPE_TIMESTAMP, 730 | 0, Frame->CurrQueryIndex, Frame->CounterBuffer, 0); 731 | 732 | DXOP(CmdList->Close()); 733 | 734 | ID3D12CommandList *CmdLists[] = {CmdList}; 735 | CmdQueue->ExecuteCommandLists(1, CmdLists); 736 | 737 | SwapChain->Present(1, 0); 738 | 739 | UINT NextBackbufferIndex = SwapChain->GetCurrentBackBufferIndex(); 740 | Context.WaitForGpu(NextBackbufferIndex); 741 | 742 | FrameData[NextBackbufferIndex].ReadbackCounters(); 743 | 744 | FrameIndex += 1; 745 | PrevCamera = Camera; 746 | } -------------------------------------------------------------------------------- /code/sdf_engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | internal void Win32Panic(char *Fmt, ...); 11 | internal void Win32MessageBox(char *Title, UINT Type, char *Fmt, ...); 12 | 13 | #include "d3d12_utils.h" 14 | #include "ch_math.h" 15 | 16 | #include "ui.h" 17 | 18 | #define BACKBUFFER_COUNT 2 19 | 20 | struct input 21 | { 22 | int Keys[256]; 23 | b32 MouseDown; 24 | v2i MouseP; 25 | v2i MousedP; 26 | 27 | b32 MouseDataIsInitialized; 28 | }; 29 | 30 | struct camera 31 | { 32 | v3 P; 33 | quaternion Orientation; 34 | }; 35 | 36 | struct profile_session 37 | { 38 | char *Name; 39 | int BeginIndex; 40 | int EndIndex; 41 | }; 42 | 43 | struct profile_result 44 | { 45 | char *Name; 46 | f32 MilisecondsElapsed; 47 | }; 48 | 49 | struct frame_data 50 | { 51 | u64 TimestampFrequency; 52 | 53 | ID3D12QueryHeap *QueryHeap; 54 | int CurrQueryIndex; 55 | int QueryCount; 56 | ID3D12Resource *CounterBuffer; 57 | 58 | profile_session Sessions[1000]; 59 | int CurrSessionIndex; 60 | bool IsInSession; 61 | 62 | void BeginProfile(ID3D12GraphicsCommandList *CmdList, char *Identifer); 63 | void EndProfile(ID3D12GraphicsCommandList *CmdList); 64 | void ReadbackCounters(); 65 | }; 66 | 67 | struct engine 68 | { 69 | gpu_context Context; 70 | IDXGISwapChain3 *SwapChain; 71 | int Width; 72 | int Height; 73 | 74 | frame_data FrameData[BACKBUFFER_COUNT]; 75 | 76 | ui_system UISystem; 77 | 78 | descriptor_arena RTVArena; 79 | descriptor_arena DescriptorArena; 80 | 81 | pso PrimaryPSO; 82 | pso PathTracePSO; 83 | pso ComputeDisocclusionPSO; 84 | pso TemporalFilterPSO; 85 | pso CalcVariancePSO; 86 | pso PackGBufferPSO; 87 | pso CorrelateHistoryPSO; 88 | pso SpatialFilterPSO; 89 | pso ApplyPrimaryShadingPSO; 90 | pso TaaPSO; 91 | pso ToneMapPSO; 92 | 93 | // pathtracer output 94 | texture NoisyLightTex; 95 | texture PositionTex; 96 | texture NormalTex; 97 | texture AlbedoTex; 98 | texture EmissionTex; 99 | texture RayDirTex; 100 | texture BlueNoiseTexs[64]; 101 | texture DisocclusionTex; 102 | 103 | // denoiser data 104 | texture PositionHistTex; 105 | texture NormalHistTex; 106 | texture LightHistTex; 107 | texture LumMomentTex; 108 | texture LumMomentHistTex; 109 | texture VarianceTex; 110 | texture NextVarianceTex; 111 | texture GBufferTex; 112 | texture PrevPixelIdTex; 113 | texture IntegratedLightTex; 114 | texture TempTex; 115 | 116 | // TAA data 117 | v2 HaltonSequence[8]; 118 | texture TaaHistTex; 119 | texture TaaOutputTex; 120 | 121 | texture OutputTex; 122 | texture BackBufferTexs[BACKBUFFER_COUNT]; 123 | 124 | camera Camera; 125 | camera PrevCamera; 126 | int SwitchViewThreshold; 127 | 128 | float Time; 129 | int FrameIndex; 130 | b32 IsInitialized; 131 | 132 | void UpdateAndRender(HWND Window, int ClientWidth, int ClientHeight, 133 | input *Input, b32 NeedsReload, f32 dT); 134 | void InitOrResizeWindowDependentResources(); 135 | }; 136 | -------------------------------------------------------------------------------- /code/spatial_filter.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), DescriptorTable(UAV(u5)), RootConstants(num32BitConstants=5, b0)" 2 | 3 | #include "math.hlsl" 4 | #include "edge_avoiding_functions.hlsl" 5 | 6 | RWTexture2D InputTex: register(u0); 7 | RWTexture2D OutputTex: register(u1); 8 | 9 | RWTexture2D GBufferTex: register(u2); 10 | 11 | RWTexture2D VarianceTex: register(u3); 12 | RWTexture2D NextVarianceTex: register(u4); 13 | 14 | RWTexture2D LightHistTex: register(u5); 15 | 16 | struct context 17 | { 18 | float3 CamP; 19 | int Depth; 20 | int Stride; 21 | }; 22 | 23 | ConstantBuffer Context: register(b0); 24 | 25 | float FetchFilteredStdDeviation(int2 ThreadId) 26 | { 27 | const float Gaussians[2] = {0.44198, 0.27901}; 28 | 29 | float FilteredVariance = 0; 30 | float TotalContrib = 0; 31 | 32 | for (int dY = -1; dY <= 1; ++dY) 33 | { 34 | for (int dX = -1; dX <= 1; ++dX) 35 | { 36 | float W = Gaussians[abs(dX)]*Gaussians[abs(dY)]; 37 | FilteredVariance += W * VarianceTex[ThreadId + int2(dX, dY)]; 38 | TotalContrib += W; 39 | } 40 | } 41 | 42 | FilteredVariance /= TotalContrib; 43 | float FilteredStdDeviation = sqrt(FilteredVariance); 44 | return FilteredStdDeviation; 45 | } 46 | 47 | [RootSignature(RS)] 48 | [numthreads(32, 32, 1)] 49 | void main(uint2 ThreadId: SV_DispatchThreadID) 50 | { 51 | float4 CenterCol = InputTex[ThreadId]; 52 | 53 | float4 Geom = GBufferTex[ThreadId]; 54 | float3 CenterNormal = Geom.xyz; 55 | float CenterDepth = Geom.w; 56 | 57 | float StdDeviation = FetchFilteredStdDeviation(ThreadId); 58 | 59 | const float Kernel[3] = {3.0/8.0, 1.0/4.0, 1/16.0}; 60 | 61 | float FilteredVariance = 0.0; 62 | float4 Filtered = 0.0; 63 | float TotalContrib = 0.0; 64 | for (int dY = -1; dY <= 1; ++dY) 65 | { 66 | for (int dX = -1; dX <= 1; ++dX) 67 | { 68 | int2 Coord = int2(ThreadId) + Context.Stride * int2(dX, dY); 69 | float4 Tap = InputTex[Coord]; 70 | float4 TapGeom = GBufferTex[Coord]; 71 | 72 | float3 TapNormal = TapGeom.xyz; 73 | float TapDepth = TapGeom.w; 74 | 75 | float W = Kernel[abs(dX)]*Kernel[abs(dY)]; 76 | 77 | W *= DepthWeight(CenterDepth, TapDepth); 78 | W *= NormalWeight(CenterNormal, TapNormal); 79 | W *= LumWeight(CenterCol, Tap, StdDeviation); 80 | 81 | Filtered += W * Tap; 82 | FilteredVariance += W*W * VarianceTex[Coord]; 83 | TotalContrib += W; 84 | } 85 | } 86 | 87 | if (TotalContrib > 0.0) 88 | { 89 | Filtered /= TotalContrib; 90 | FilteredVariance /= TotalContrib*TotalContrib; 91 | } 92 | 93 | OutputTex[ThreadId] = Filtered; 94 | NextVarianceTex[ThreadId] = FilteredVariance; 95 | 96 | if (Context.Depth == 0) 97 | { 98 | LightHistTex[ThreadId] = Filtered; 99 | } 100 | } -------------------------------------------------------------------------------- /code/sphere2.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | point_query Map(float3 P, float Time) 4 | { 5 | point_query Q; 6 | Q.Dist = 10e31; 7 | Q.MatId = -1; 8 | 9 | float Floor = P.y + 1.0; 10 | float Sphere = length(P) - 1.0; 11 | float Sphere2 = length(P - float3(2.5 * sin(Time), 2, 2.5 * cos(Time))) - 1.0; 12 | 13 | BIND(Floor, 0); 14 | BIND(Sphere, 1); 15 | BIND(Sphere2, 2); 16 | 17 | return Q; 18 | } 19 | 20 | material MapMaterial(int MatId, float3 P, float Time) 21 | { 22 | material Mat; 23 | Mat.Albedo = 0.9; 24 | Mat.Emission = 0; 25 | 26 | if (MatId == 0) // floor 27 | { 28 | Mat.Albedo = 0.5; 29 | } 30 | else if (MatId == 1) // sphere 31 | { 32 | Mat.Albedo = float3(0.8, 0.2, 0.2); 33 | } 34 | else if (MatId == 2) 35 | { 36 | Mat.Emission = 8; 37 | } 38 | 39 | return Mat; 40 | } 41 | 42 | float3 Env(float3 Rd, float Time) 43 | { 44 | return 0.0 * float3(0.3, 0.4, 0.5); 45 | } 46 | 47 | float Fog(float Depth) 48 | { 49 | return 1.0; 50 | } 51 | 52 | float3 SunDir() 53 | { 54 | return normalize(float3(-0.2, 0.3, 0.1)); 55 | } 56 | 57 | float3 SunLight() 58 | { 59 | return 0; 60 | } 61 | 62 | float SunAperture() 63 | { 64 | return 0.1; 65 | } -------------------------------------------------------------------------------- /code/taa.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), RootConstants(num32BitConstants=3, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D InputTex: register(u0); 6 | RWTexture2D HistTex: register(u1); 7 | RWTexture2D OutputTex: register(u2); 8 | RWTexture2D PrevPixelIdTex: register(u3); 9 | 10 | struct context 11 | { 12 | int FrameIndex; 13 | int Width; 14 | int Height; 15 | uint Pad; 16 | }; 17 | 18 | ConstantBuffer Context: register(b0); 19 | 20 | [RootSignature(RS)] 21 | [numthreads(32, 32, 1)] 22 | void main(uint2 ThreadId: SV_DispatchThreadID) 23 | { 24 | float2 PrevPixelId = PrevPixelIdTex[ThreadId]; 25 | 26 | if (Context.FrameIndex != 0 && 27 | all(PrevPixelId >= 0.0 && PrevPixelId <= float2(Context.Width-1, Context.Height-1))) 28 | { 29 | float2 Interp = frac(PrevPixelId); 30 | int2 PrevPixelCoord = floor(PrevPixelId); 31 | 32 | float3 FilteredHist = 0; 33 | for (int dY = 0; dY <= 1; ++dY) 34 | { 35 | for (int dX = 0; dX <= 1; ++dX) 36 | { 37 | int2 TapCoord = PrevPixelCoord + int2(dX, dY); 38 | float2 Bilinear = lerp(1.0-Interp, Interp, float2(dX, dY)); 39 | float W = Bilinear.x * Bilinear.y; 40 | FilteredHist += W * HistTex[TapCoord].rgb; 41 | } 42 | } 43 | 44 | float3 BoxNeighborMin = 10e31; 45 | float3 BoxNeighborMax = -10e31; 46 | 47 | for (int dY = -1; dY <= 1; ++dY) 48 | { 49 | for (int dX = -1; dX <= 1; ++dX) 50 | { 51 | int2 TapCoord = ThreadId + int2(dX, dY); 52 | float3 Tap = TaaTonemap(InputTex[TapCoord].rgb); 53 | BoxNeighborMin = min(BoxNeighborMin, Tap); 54 | BoxNeighborMax = max(BoxNeighborMax, Tap); 55 | } 56 | } 57 | 58 | FilteredHist = clamp(FilteredHist, BoxNeighborMin, BoxNeighborMax); 59 | 60 | float Alpha = 0.1; 61 | float3 IntegratedCol = lerp(FilteredHist, 62 | TaaTonemap(InputTex[ThreadId].rgb), 63 | Alpha); 64 | OutputTex[ThreadId] = float4(IntegratedCol, 1.0); 65 | } 66 | else 67 | { 68 | OutputTex[ThreadId] = float4(TaaTonemap(InputTex[ThreadId].rgb), 1.0); 69 | } 70 | } -------------------------------------------------------------------------------- /code/temporal_filter.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(UAV(u3)), DescriptorTable(UAV(u4)), DescriptorTable(UAV(u5)), DescriptorTable(UAV(u6)), DescriptorTable(UAV(u7)), DescriptorTable(UAV(u8)), DescriptorTable(UAV(u9)), RootConstants(num32BitConstants=10, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D InputTex: register(u0); 6 | RWTexture2D LightHistTex: register(u1); 7 | RWTexture2D FilteredTex: register(u2); 8 | 9 | RWTexture2D PositionTex: register(u3); 10 | RWTexture2D NormalTex: register(u4); 11 | 12 | RWTexture2D PositionHistTex: register(u5); 13 | RWTexture2D NormalHistTex: register(u6); 14 | 15 | RWTexture2D LumMomentHistTex: register(u7); 16 | RWTexture2D LumMomentTex: register(u8); 17 | 18 | RWTexture2D PrevPixelIdTex: register(u9); 19 | 20 | struct context 21 | { 22 | int FrameIndex; 23 | float3 CamP; 24 | float4 CamInvQuat; 25 | int Width; 26 | int Height; 27 | }; 28 | 29 | ConstantBuffer Context: register(b0); 30 | 31 | [RootSignature(RS)] 32 | [numthreads(32, 32, 1)] 33 | void main(uint2 ThreadId: SV_DispatchThreadID) 34 | { 35 | float3 P = PositionTex[ThreadId].xyz; 36 | float3 CurrN = NormalTex[ThreadId].xyz; 37 | float2 PrevPixelId = PrevPixelIdTex[ThreadId]; 38 | 39 | float CenterLum = CalcLuminance(InputTex[ThreadId].rgb); 40 | float2 LumMoments = float2(CenterLum, CenterLum * CenterLum); 41 | 42 | if (Context.FrameIndex != 0 && 43 | all(PrevPixelId >= 0.0 && PrevPixelId <= float2(Context.Width-1, Context.Height-1))) 44 | { 45 | float2 Interp = frac(PrevPixelId); 46 | int2 PrevPixelCoord = floor(PrevPixelId); 47 | 48 | float MinSampleCount = 10e31; 49 | float2 LumMomentHist = 0; 50 | float3 FilteredHist = 0; 51 | float TotalContrib = 0; 52 | for (int dY = 0; dY <= 1; ++dY) 53 | { 54 | for (int dX = 0; dX <= 1; ++dX) 55 | { 56 | int2 TapCoord = PrevPixelCoord + int2(dX, dY); 57 | float3 PrevN = NormalHistTex[TapCoord].xyz; 58 | float3 PrevP = PositionHistTex[TapCoord].xyz; 59 | float PrevDepth = length(Context.CamP - PrevP); 60 | float CurrDepth = length(Context.CamP - P); 61 | if (abs(1.0 - CurrDepth/PrevDepth) < 0.1 && 62 | dot(CurrN, PrevN) >= 0.99) 63 | { 64 | float2 Bilinear = lerp(1.0-Interp, Interp, float2(dX, dY)); 65 | float W = Bilinear.x * Bilinear.y; 66 | FilteredHist += W * LightHistTex[TapCoord].rgb; 67 | LumMomentHist += W *LumMomentHistTex[TapCoord]; 68 | TotalContrib += W; 69 | 70 | float TapSampleCount = LightHistTex[TapCoord].a; 71 | MinSampleCount = min(MinSampleCount, TapSampleCount); 72 | } 73 | } 74 | } 75 | 76 | if (TotalContrib > 0.0) 77 | { 78 | FilteredHist /= TotalContrib; 79 | LumMomentHist /= TotalContrib; 80 | 81 | float Alpha = 0.1; 82 | 83 | float3 FilteredCol = lerp(FilteredHist.rgb, InputTex[ThreadId].rgb, Alpha); 84 | 85 | float EffectiveSampleCount = MinSampleCount + 1; 86 | FilteredTex[ThreadId] = float4(FilteredCol, EffectiveSampleCount); 87 | 88 | LumMomentTex[ThreadId] = lerp(LumMomentHist, LumMoments, Alpha); 89 | } 90 | else 91 | { 92 | float SampleCount = 1; 93 | FilteredTex[ThreadId] = float4(InputTex[ThreadId].rgb, SampleCount); 94 | LumMomentTex[ThreadId] = LumMoments; 95 | } 96 | } 97 | else 98 | { 99 | float SampleCount = 1; 100 | FilteredTex[ThreadId] = float4(InputTex[ThreadId].rgb, SampleCount); 101 | LumMomentTex[ThreadId] = LumMoments; 102 | } 103 | } -------------------------------------------------------------------------------- /code/todo.txt: -------------------------------------------------------------------------------- 1 | - create D3D device resources [x] 2 | - debug layer [x] 3 | - command context [x] 4 | - descriptor resources [x] 5 | - compute pso [x] 6 | - texture [x] 7 | - fence [x] 8 | 9 | - dynamic camera [x] 10 | - input handling [x] 11 | - import math code [x] 12 | - camera control [x] 13 | 14 | - denoiser [x] 15 | - output aux buffer [x] 16 | - temporal filter [x] 17 | - spatial filter [x] 18 | - backproj [x] 19 | - backproj rectification [x] 20 | - bilinear filter backproj [x] 21 | - variance-guidance [x] 22 | - fallback strategy for disocclusion regions [x] 23 | - feedback first-level wavelet to temporal filter [x] 24 | - optimize spatial filter [x] 25 | 26 | - TAA [x] 27 | - pull out history pixel coord correlation [x] 28 | - basic TAA [x] 29 | - jitter/unjitter [x] 30 | - tonemap in, inv-tonemap out [x] 31 | 32 | - apply back first-hit BRDF [x] 33 | - apply primary brdf [x] 34 | - apply primary emission [x] 35 | - apply Env() on sky [x] 36 | 37 | - shader hotload [x] 38 | - timestamp check & reload [x] 39 | - basic leak & reload [x] 40 | - hook reload into all hlsl files [x] 41 | - gracefully reload [x] 42 | - semi-gracefully spit error msg ... [x] 43 | 44 | - load blue noise textures [x] 45 | - ditter to remove banding [x] 46 | 47 | - halton TAA sampling [x] 48 | 49 | - shoot more rays in disoccluded area [x] 50 | - separate primary from indirect [x] 51 | - adaptive sampling [x] 52 | - account for history out of range [x] 53 | - account for failed backprojection [x] 54 | 55 | - proper platform layer [x] 56 | - refresh state if window goes inactive [x] 57 | - handle mouse moving-outside of screen [x] 58 | 59 | - proper CPU/GPU pipelining [x] 60 | 61 | - window resize/fullscreen [x] 62 | 63 | - framerate-independence [x] 64 | 65 | - user-defined fog function [x] 66 | 67 | - UI system [x] 68 | - rasterize ascii font atlas [x] 69 | - refactor PSO creation & verification code [x] 70 | - create graphics PSO creation routine [x] 71 | - build PSO for text rasterization [x] 72 | - routine for building texture-mapped text blocks [x] 73 | - system for multi-line error msg display [x] 74 | - translucent background for text [x] 75 | - dynamic resolution [x] 76 | 77 | - finish tunnel [x] 78 | 79 | - directional light source support [x] 80 | - need perf counters [x] 81 | - wrap up per-frame profiling [x] 82 | 83 | - optimizations [] 84 | - enhanced raymarch [x] 85 | - optimized filters [] 86 | 87 | - pull out low-level graphics library [] 88 | 89 | - more complex materials [] 90 | - specular/metallic [] 91 | - translucent/refraction [] 92 | 93 | - multi-temporal-filter approach [] 94 | - path-space filtering [] 95 | 96 | - temporal backproj improvement [] 97 | - use temporal distortion factor [] 98 | 99 | - next event estimation [] 100 | - try cached point clouds [] 101 | 102 | - fix TAA bug [] 103 | - border pixels flicker [] 104 | - check flickering in tron [] 105 | 106 | - better noise [] 107 | 108 | - volumetric lighting [] 109 | - fog particles [] 110 | 111 | - generate blue noise with even temporal distribution [] 112 | -------------------------------------------------------------------------------- /code/tonemap.hlsl: -------------------------------------------------------------------------------- 1 | #define RS "DescriptorTable(UAV(u0)), DescriptorTable(UAV(u1)), DescriptorTable(UAV(u2)), DescriptorTable(SRV(t0, numDescriptors=64)), RootConstants(num32BitConstants=2, b0)" 2 | 3 | #include "math.hlsl" 4 | 5 | RWTexture2D InputTex: register(u0); 6 | RWTexture2D OutputTex: register(u1); 7 | RWTexture2D NoisyInputTex: register(u2); 8 | 9 | Texture2D BlueNoiseTexs[64]: register(t0); 10 | 11 | struct context 12 | { 13 | int Threshold; 14 | int FrameIndex; 15 | }; 16 | 17 | ConstantBuffer Context: register(b0); 18 | 19 | [RootSignature(RS)] 20 | [numthreads(32, 32, 1)] 21 | void main(uint2 ThreadId: SV_DispatchThreadID) 22 | { 23 | float3 Col = TaaInvTonemap(InputTex[ThreadId].rgb); 24 | 25 | if (ThreadId.x < Context.Threshold) 26 | { 27 | float3 NoisyCol = NoisyInputTex[ThreadId].rgb; 28 | Col = NoisyCol; 29 | } 30 | 31 | Col *= 1.0; 32 | Col = Col/(1.0+Col); 33 | Col = sqrt(Col); 34 | 35 | // dither to remove banding 36 | float R = BlueNoiseTexs[Context.FrameIndex & 63][ThreadId & 63].r; 37 | Col += R * (1.0/255.0); 38 | 39 | OutputTex[ThreadId] = float4(Col, 1.0); 40 | } -------------------------------------------------------------------------------- /code/tron.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | point_query Map(float3 P, float Time) 4 | { 5 | point_query Q; 6 | Q.Dist = 10e31; 7 | Q.MatId = -1; 8 | 9 | P.z += 1.0; 10 | P = fmod(P-2.5, 5.0)+2.5; 11 | 12 | float Floor = P.y + 1.0; 13 | float Sphere = length(P) - 1.0; 14 | 15 | BIND(Floor, 0); 16 | BIND(Sphere, 1); 17 | 18 | return Q; 19 | } 20 | 21 | material MapMaterial(int MatId, float3 P, float Time) 22 | { 23 | material Mat; 24 | Mat.Albedo = 0.9; 25 | Mat.Emission = 0; 26 | 27 | if (MatId == 0) 28 | { 29 | Mat.Albedo = 0.8; 30 | } 31 | else if (MatId == 1) 32 | { 33 | Mat.Albedo = 1; 34 | 35 | P.z += 1.0; 36 | P = fmod(P-2.5, 5.0)+2.5; 37 | if (abs(P.x) < 0.1) Mat.Emission = P+1.0; 38 | } 39 | 40 | return Mat; 41 | } 42 | 43 | float3 Env(float3 Rd, float Time) 44 | { 45 | return 0; 46 | } 47 | 48 | float Fog(float Depth) 49 | { 50 | return 1.0; 51 | } 52 | 53 | float3 SunDir() 54 | { 55 | return normalize(float3(-0.2, 0.3, 0.1)); 56 | } 57 | 58 | float3 SunLight() 59 | { 60 | return 0; 61 | } 62 | 63 | float SunAperture() 64 | { 65 | return 0.1; 66 | } -------------------------------------------------------------------------------- /code/tunnel.hlsl: -------------------------------------------------------------------------------- 1 | #define BIND(D, Id) Q.Dist = min(Q.Dist, D); if (Q.Dist == D) Q.MatId = Id; 2 | 3 | float hash(in float x) 4 | { 5 | return frac(sin(13.15*x)*932.3); 6 | } 7 | 8 | float hash2(in float2 p) 9 | { 10 | return hash(dot(p, float2(91.3, 151.16))); 11 | } 12 | 13 | float hash3(in float3 p) 14 | { 15 | return hash(dot(p, float3(91.3, 151.16, 72.15))); 16 | } 17 | 18 | float noise3(in float3 p) 19 | { 20 | float3 ipos = floor(p); 21 | float3 fpos = frac(p); 22 | 23 | float a = hash3(ipos + float3(0, 0, 0)); 24 | float b = hash3(ipos + float3(1, 0, 0)); 25 | float c = hash3(ipos + float3(0, 1, 0)); 26 | float d = hash3(ipos + float3(1, 1, 0)); 27 | float e = hash3(ipos + float3(0, 0, 1)); 28 | float f = hash3(ipos + float3(1, 0, 1)); 29 | float g = hash3(ipos + float3(0, 1, 1)); 30 | float h = hash3(ipos + float3(1, 1, 1)); 31 | 32 | float3 t = smoothstep(0, 1, fpos); 33 | 34 | return lerp(lerp(lerp(a, b, t.x), lerp(c, d, t.x), t.y), 35 | lerp(lerp(e, f, t.x), lerp(g, h, t.x), t.y), 36 | t.z); 37 | } 38 | 39 | float fbm3(in float3 p) 40 | { 41 | float res = 0.0; 42 | float amp = 0.5; 43 | float freq = 2.0; 44 | for (int i = 0; i < 6; ++i) 45 | { 46 | res += amp*noise3(freq*p); 47 | amp *= 0.5; 48 | freq *= 2.0; 49 | } 50 | return res; 51 | } 52 | 53 | float noise2(in float2 p) 54 | { 55 | float2 ipos = floor(p); 56 | float2 fpos = frac(p); 57 | 58 | float a = hash2(ipos + float2(0, 0)); 59 | float b = hash2(ipos + float2(1, 0)); 60 | float c = hash2(ipos + float2(0, 1)); 61 | float d = hash2(ipos + float2(1, 1)); 62 | 63 | float2 t = smoothstep(0, 1, fpos); 64 | 65 | return lerp(lerp(a, b, t.x), lerp(c, d, t.x), t.y); 66 | } 67 | 68 | float fbm2(in float2 p) 69 | { 70 | float res = 0.0; 71 | float amp = 0.5; 72 | float freq = 2.0; 73 | for (int i = 0; i < 6; ++i) 74 | { 75 | res += amp*noise2(freq*p); 76 | amp *= 0.5; 77 | freq *= 2.0; 78 | } 79 | return res; 80 | } 81 | 82 | point_query Map(float3 P, float Time) 83 | { 84 | point_query Q; 85 | Q.Dist = 10e31; 86 | Q.MatId = -1; 87 | 88 | float Floor = P.y + 1.0; 89 | float Sphere = length(P) - 1.0; 90 | 91 | float Tunnel = -(length(P.xy) - 5.0); 92 | if (Tunnel < 0.2) 93 | { 94 | float U = frac(P.z); 95 | float V = frac(10.0*atan2(P.y, P.x)); 96 | float UMod = smoothstep(0.0, 0.2, U) * smoothstep(0.8, 1.0, U); 97 | float VMod = smoothstep(0.0, 0.2, V) * smoothstep(0.8, 1.0, V); 98 | Tunnel += 0.1 * (UMod + VMod); 99 | } 100 | Tunnel = abs(Tunnel) - 0.1; 101 | float Freq = 8.0; 102 | float Neg = length(P - float3(-5, 2, 0)) - 3.0 + 0.1*sin(Freq*P.x)*sin(Freq*P.y)*sin(Freq*P.z); 103 | Tunnel = max(Tunnel, -Neg); 104 | 105 | BIND(Floor, 0); 106 | BIND(Sphere, 1); 107 | BIND(Tunnel, 2); 108 | 109 | return Q; 110 | } 111 | 112 | material MapMaterial(int MatId, float3 P, float Time) 113 | { 114 | material Mat; 115 | Mat.Albedo = 0.9; 116 | Mat.Emission = 0; 117 | 118 | if (MatId == 0) // floor 119 | { 120 | Mat.Albedo = 0.1; 121 | 122 | float2 Deterioration = 0.1*fbm2(5.0*P.xz); 123 | if (abs(P.x) < 0.3+Deterioration.x && 124 | frac(0.1*P.z) < 0.8+Deterioration.y) 125 | { 126 | Mat.Albedo = float3(0.8, 0.8, 0.1); 127 | } 128 | } 129 | else if (MatId == 1) // sphere 130 | { 131 | Mat.Emission = 0; 132 | } 133 | else if (MatId == 2) // tunnel 134 | { 135 | Mat.Albedo = float3(0.7, 0.4, 0.3); 136 | } 137 | 138 | return Mat; 139 | } 140 | 141 | float3 Env(float3 Rd, float Time) 142 | { 143 | return 0.0 * float3(0.3, 0.4, 0.5); 144 | } 145 | 146 | float Fog(float Depth) 147 | { 148 | return pow(clamp(1.0 - Depth / 80.0, 0, 1), 0.5); 149 | } 150 | 151 | float3 SunDir() 152 | { 153 | return normalize(float3(-0.2, 0.3, 0.1)); 154 | } 155 | 156 | float3 SunLight() 157 | { 158 | return float3(3.0, 3.0, 3.0); 159 | } 160 | 161 | float SunAperture() 162 | { 163 | return 0.1; 164 | } -------------------------------------------------------------------------------- /code/ui.cpp: -------------------------------------------------------------------------------- 1 | internal ui_system 2 | InitUISystem(gpu_context *Context, descriptor_arena *DescriptorArena) 3 | { 4 | ui_system System = {}; 5 | 6 | stbtt_fontinfo FontInfo = {}; 7 | 8 | char *FontFilePath = "../data/liberation-mono.ttf"; 9 | FILE *FontFile = fopen(FontFilePath, "rb"); 10 | if (!FontFile) 11 | { 12 | Win32Panic("Failed to load font file located at %s", FontFilePath); 13 | } 14 | fseek(FontFile, 0, SEEK_END); 15 | int FileSize = ftell(FontFile); 16 | rewind(FontFile); 17 | u8 *FileData = (u8 *)calloc(FileSize, 1); 18 | fread(FileData, 1, FileSize, FontFile); 19 | fclose(FontFile); 20 | 21 | stbtt_InitFont(&FontInfo, FileData, 0); 22 | 23 | int BeginCharI = 32; 24 | int EndCharI = 126; 25 | int FontHeight = 50; 26 | 27 | f32 Scale = stbtt_ScaleForPixelHeight(&FontInfo, f32(FontHeight)); 28 | int Ascent, Descent, LineGap; 29 | stbtt_GetFontVMetrics(&FontInfo, &Ascent, &Descent, &LineGap); 30 | int Baseline = int(f32(Ascent) * Scale); 31 | 32 | //NOTE(chen): ensure the font is monospace 33 | int AdvanceWidth, LeftBearing; 34 | stbtt_GetCodepointHMetrics(&FontInfo, BeginCharI, &AdvanceWidth, &LeftBearing); 35 | for (int CharI = BeginCharI; CharI <= EndCharI; ++CharI) 36 | { 37 | int CurrAdvanceWidth, CurrLeftBearing; 38 | stbtt_GetCodepointHMetrics(&FontInfo, CharI, &CurrAdvanceWidth, &CurrLeftBearing); 39 | ASSERT(AdvanceWidth == CurrAdvanceWidth); 40 | } 41 | int FontWidth = int(f32(AdvanceWidth) * Scale); 42 | 43 | int CharCount = EndCharI - BeginCharI + 1; 44 | int CharXCount = int(SquareRoot(f32(CharCount))); 45 | int CharYCount = Ceil(f32(CharCount) / f32(CharXCount)); 46 | 47 | // allocate bitmap adaptively based on font metrics 48 | int AtlasWidth = CharXCount * FontWidth; 49 | int AtlasHeight = CharYCount * FontHeight; 50 | u8 *AtlasData = (u8 *)calloc(AtlasWidth*AtlasHeight, 1); 51 | 52 | // bake characters 53 | for (int CharI = BeginCharI; CharI <= EndCharI; ++CharI) 54 | { 55 | char Char = char(CharI); 56 | 57 | int X0, Y0, X1, Y1; 58 | stbtt_GetCodepointBitmapBox(&FontInfo, Char, Scale, Scale, &X0, &Y0, &X1, &Y1); 59 | 60 | int Index = CharI - BeginCharI; 61 | int X = (Index % CharXCount) * FontWidth + X0; 62 | int Y = (Index / CharXCount) * FontHeight + (Baseline + Y0); 63 | 64 | u8 *Out = AtlasData + X + Y*AtlasWidth; 65 | stbtt_MakeCodepointBitmap(&FontInfo, Out, 66 | X1-X0, Y1-Y0, AtlasWidth, 67 | Scale, Scale, CharI); 68 | } 69 | free(FileData); 70 | 71 | System.BeginCharI = BeginCharI; 72 | System.EndCharI = EndCharI; 73 | System.CharXCount = CharXCount; 74 | System.CharYCount = CharYCount; 75 | System.FontWidth = FontWidth; 76 | System.FontHeight = FontHeight; 77 | System.AtlasWidth = AtlasWidth; 78 | System.AtlasHeight = AtlasHeight; 79 | System.FontAtlas = InitTexture2D(Context->Device, 80 | AtlasWidth, AtlasHeight, 81 | DXGI_FORMAT_R8_UNORM, D3D12_RESOURCE_FLAG_NONE, 82 | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 83 | Context->Upload(&System.FontAtlas, AtlasData); 84 | AssignSRV(Context->Device, &System.FontAtlas, DescriptorArena); 85 | free(AtlasData); 86 | 87 | D3D12_INPUT_ELEMENT_DESC TextVertInputElements[] = { 88 | {"POS", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, 89 | {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, 90 | {"TYPE", 0, DXGI_FORMAT_R32G32_SINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, 91 | }; 92 | 93 | auto Edit = [](D3D12_GRAPHICS_PIPELINE_STATE_DESC *PSODesc) { 94 | PSODesc->DepthStencilState.DepthEnable = false; 95 | PSODesc->RasterizerState.CullMode = D3D12_CULL_MODE_NONE; 96 | 97 | D3D12_RENDER_TARGET_BLEND_DESC *RTBlendDesc = PSODesc->BlendState.RenderTarget; 98 | RTBlendDesc->BlendEnable = TRUE; 99 | RTBlendDesc->SrcBlend = D3D12_BLEND_SRC_ALPHA; 100 | RTBlendDesc->DestBlend = D3D12_BLEND_INV_SRC_ALPHA; 101 | RTBlendDesc->BlendOp = D3D12_BLEND_OP_ADD; 102 | RTBlendDesc->SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; 103 | RTBlendDesc->DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; 104 | RTBlendDesc->BlendOpAlpha = D3D12_BLEND_OP_ADD; 105 | }; 106 | System.RasterizeTextPSO = InitGraphicsPSO(Context->Device, 107 | "../code/rasterize_text.hlsl", 108 | TextVertInputElements, 109 | ARRAY_COUNT(TextVertInputElements), 110 | Edit); 111 | 112 | return System; 113 | } 114 | 115 | void 116 | ui_system::SetErrorMessage(char *String) 117 | { 118 | if (Message) 119 | { 120 | free(Message); 121 | } 122 | Message = strdup(String); 123 | } 124 | 125 | void 126 | ui_system::BuildUIVB(gpu_context *Context, int Width, int Height) 127 | { 128 | int MessageLen = Message? int(strlen(Message)): 0; 129 | if (MessageLen == 0) 130 | { 131 | UIVertCount = 0; 132 | if (UIVB) 133 | { 134 | Context->FlushFramesInFlight(); 135 | UIVB->Release(); 136 | UIVB = 0; 137 | } 138 | 139 | return; 140 | } 141 | 142 | int LineCount = 1; 143 | for (int I = 0; I < MessageLen; ++I) 144 | { 145 | if (Message[I] == '\n') 146 | { 147 | LineCount += 1; 148 | } 149 | } 150 | 151 | v2 FontDim = V2(f32(FontWidth)/f32(Width), f32(FontHeight)/f32(Height)); 152 | v2 Offset = {-1.0f, -1.0f + f32(LineCount)*FontDim.Y}; 153 | 154 | UIVertCount = 6 + 6 * MessageLen; 155 | ui_vertex *TextVertices = (ui_vertex *)calloc(UIVertCount, sizeof(ui_vertex)); 156 | 157 | // build verts 158 | int Cursor = 0; 159 | 160 | // build background verts 161 | { 162 | v2 TopLeftP = Offset + V2(0.0f, FontDim.Y); 163 | v2 BotRightP = Offset + V2(2.0f, (f32(LineCount)) * -FontDim.Y); 164 | 165 | TextVertices[Cursor++] = {TopLeftP, TopLeftP, 1}; 166 | TextVertices[Cursor++] = {V2(BotRightP.X, TopLeftP.Y), V2(BotRightP.X, TopLeftP.Y), 1}; 167 | TextVertices[Cursor++] = {V2(TopLeftP.X, BotRightP.Y), V2(TopLeftP.X, BotRightP.Y), 1}; 168 | TextVertices[Cursor++] = {V2(TopLeftP.X, BotRightP.Y), V2(TopLeftP.X, BotRightP.Y), 1}; 169 | TextVertices[Cursor++] = {BotRightP, BotRightP, 1}; 170 | TextVertices[Cursor++] = {V2(BotRightP.X, TopLeftP.Y), V2(BotRightP.X, TopLeftP.Y), 1}; 171 | } 172 | 173 | // build text verts 174 | for (int CharI = 0; CharI < MessageLen; ++CharI) 175 | { 176 | int Code = Message[CharI]; 177 | 178 | if (Code >= BeginCharI && Code <= EndCharI) 179 | { 180 | int Index = Code - BeginCharI; 181 | 182 | int CodePointX = (Index % CharXCount) * FontWidth; 183 | int CodePointY = (Index / CharXCount) * FontHeight; 184 | 185 | f32 U = f32(CodePointX) / f32(AtlasWidth); 186 | f32 V = f32(CodePointY) / f32(AtlasHeight); 187 | f32 UVWidth = f32(FontWidth) / f32(AtlasWidth); 188 | f32 UVHeight = f32(FontHeight) / f32(AtlasHeight); 189 | 190 | v2 TopLeftP = Offset; 191 | v2 BotRightP = TopLeftP + V2(FontDim.X, -FontDim.Y); 192 | 193 | v2 TopLeftUV = V2(U, V); 194 | v2 BotRightUV = TopLeftUV + V2(UVWidth, UVHeight); 195 | 196 | TextVertices[Cursor++] = {TopLeftP, TopLeftUV, 0}; 197 | TextVertices[Cursor++] = {V2(BotRightP.X, TopLeftP.Y), V2(BotRightUV.X, TopLeftUV.Y), 0}; 198 | TextVertices[Cursor++] = {V2(TopLeftP.X, BotRightP.Y), V2(TopLeftUV.X, BotRightUV.Y), 0}; 199 | TextVertices[Cursor++] = {V2(TopLeftP.X, BotRightP.Y), V2(TopLeftUV.X, BotRightUV.Y), 0}; 200 | TextVertices[Cursor++] = {BotRightP, BotRightUV, 0}; 201 | TextVertices[Cursor++] = {V2(BotRightP.X, TopLeftP.Y), V2(BotRightUV.X, TopLeftUV.Y), 0}; 202 | 203 | Offset.X += FontDim.X; 204 | } 205 | else if (Code == '\n') 206 | { 207 | Offset.X = -1.0f; 208 | Offset.Y -= FontDim.Y; 209 | } 210 | else 211 | { 212 | ASSERT(!"Unhandled character"); 213 | } 214 | } 215 | 216 | // upload verts 217 | if (UIVB) 218 | { 219 | Context->FlushFramesInFlight(); 220 | UIVB->Release(); 221 | UIVB = 0; 222 | } 223 | 224 | if (MessageLen > 0) 225 | { 226 | size_t UIVertSize = UIVertCount * sizeof(ui_vertex); 227 | UIVB = InitBuffer(Context->Device, UIVertSize, 228 | D3D12_HEAP_TYPE_UPLOAD, D3D12_RESOURCE_STATE_GENERIC_READ, 229 | D3D12_RESOURCE_FLAG_NONE); 230 | Upload(UIVB, TextVertices, UIVertSize); 231 | } 232 | 233 | free(TextVertices); 234 | } 235 | 236 | void 237 | ui_system::DrawMessage(gpu_context *Context, texture RenderTarget) 238 | { 239 | if (UIVertCount == 0) return; 240 | 241 | size_t UIVertSize = UIVertCount * sizeof(ui_vertex); 242 | 243 | int TargetWidth = int(RenderTarget.Handle->GetDesc().Width); 244 | int TargetHeight = RenderTarget.Handle->GetDesc().Height; 245 | 246 | D3D12_VIEWPORT Viewport = {}; 247 | Viewport.Width = f32(TargetWidth); 248 | Viewport.Height = f32(TargetHeight); 249 | Viewport.MinDepth = 0.0f; 250 | Viewport.MaxDepth = 1.0f; 251 | D3D12_RECT ScissorRect = {}; 252 | ScissorRect.right = LONG(TargetWidth); 253 | ScissorRect.bottom = LONG(TargetHeight); 254 | 255 | ID3D12GraphicsCommandList *CmdList = Context->CmdList; 256 | CmdList->SetPipelineState(RasterizeTextPSO.Handle); 257 | CmdList->SetGraphicsRootSignature(RasterizeTextPSO.RootSignature); 258 | CmdList->RSSetViewports(1, &Viewport); 259 | CmdList->RSSetScissorRects(1, &ScissorRect); 260 | 261 | CmdList->SetGraphicsRootDescriptorTable(0, FontAtlas.SRV.GPUHandle); 262 | CmdList->OMSetRenderTargets(1, &RenderTarget.RTV.CPUHandle, 0, 0); 263 | D3D12_VERTEX_BUFFER_VIEW VBView = {}; 264 | VBView.BufferLocation = UIVB->GetGPUVirtualAddress(); 265 | VBView.SizeInBytes = UINT(UIVertSize); 266 | VBView.StrideInBytes = sizeof(ui_vertex); 267 | CmdList->IASetVertexBuffers(0, 1, &VBView); 268 | CmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); 269 | CmdList->DrawInstanced(UIVertCount, 1, 0, 0); 270 | } -------------------------------------------------------------------------------- /code/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define STB_TRUETYPE_IMPLEMENTATION 4 | #include "stb_truetype.h" 5 | 6 | #define STB_IMAGE_WRITE_IMPLEMENTATION 7 | #include "stb_image_write.h" 8 | 9 | #pragma pack(push, 1) 10 | struct ui_vertex 11 | { 12 | v2 P; 13 | v2 UV; 14 | int Type; 15 | }; 16 | #pragma pack(pop) 17 | 18 | struct ui_system 19 | { 20 | texture FontAtlas; 21 | int AtlasWidth; 22 | int AtlasHeight; 23 | int BeginCharI; 24 | int EndCharI; 25 | int CharXCount; 26 | int CharYCount; 27 | int FontWidth; 28 | int FontHeight; 29 | 30 | pso RasterizeTextPSO; 31 | 32 | char *Message; 33 | ID3D12Resource *UIVB; 34 | int UIVertCount; 35 | 36 | void SetErrorMessage(char *String); 37 | void BuildUIVB(gpu_context *Context, int Width, int Height); 38 | 39 | void DrawMessage(gpu_context *Context, texture RenderTarget); 40 | }; -------------------------------------------------------------------------------- /data/liberation-mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/liberation-mono.ttf -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_0.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_1.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_10.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_11.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_12.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_13.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_14.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_15.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_16.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_17.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_18.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_19.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_2.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_20.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_21.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_22.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_23.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_24.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_25.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_26.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_27.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_28.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_29.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_3.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_30.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_31.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_32.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_33.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_34.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_35.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_36.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_37.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_38.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_39.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_4.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_40.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_41.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_42.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_43.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_44.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_45.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_46.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_47.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_48.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_49.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_5.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_50.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_51.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_52.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_53.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_54.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_55.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_56.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_57.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_58.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_59.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_6.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_60.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_61.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_62.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_63.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_7.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_8.png -------------------------------------------------------------------------------- /data/textures/HDR_RGBA_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/HDR_RGBA_9.png -------------------------------------------------------------------------------- /data/textures/LDR_LLL1_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/LDR_LLL1_0.png -------------------------------------------------------------------------------- /data/textures/LDR_LLL1_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ALEXMORF/Spectra/1bee3c05070be575de208e9d7b247e7fd1b5b4df/data/textures/LDR_LLL1_1.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Spectra 2 | 3 | A real-time SDF pathtracer. Edit your SDFs and see it pathtraced interactively. 4 | 5 | [LIVE DEMO](https://youtu.be/u7ObOkmmWpE) 6 | 7 | # Build it 8 | 9 | Load cl.exe into environment variable, then invoke code/build.bat to generate the binary. 10 | 11 | # Usage 12 | 13 | Build Spectra and run it. While the application is running, you can edit code/scene.hlsl and the changes will be hotloaded in real-time. 14 | 15 | To make this work, you need to define the following three functions in scene.hlsl file: 16 | 17 | ``` 18 | // user-defined functions: 19 | 20 | point_query Map(float3 P, float Time) 21 | { 22 | // define geometry 23 | } 24 | 25 | material MapMaterial(int MatId, float3 P, float Time) 26 | { 27 | // define material 28 | } 29 | 30 | float3 Env(float3 Rd, float Time) 31 | { 32 | // define environment lighting 33 | } 34 | 35 | float Fog(float Depth) 36 | { 37 | // define fog attenuation 38 | } 39 | 40 | ``` 41 | 42 | To see how these functions are getting called, check out raymarch.hlsl and primary_shading.hlsl. 43 | 44 | # Examples 45 | 46 | Also, the source comes with some example scenes I've made in the form of include files: 47 | 48 | ``` 49 | 50 | #include "sphere.hlsl" 51 | //#include "cornellbox.hlsl" 52 | //#include "tron.hlsl" 53 | ... 54 | ... 55 | 56 | ``` 57 | 58 | Selectively un-comment the scene you want to see. Here are some screenshots of these stock scenes I've prepared: 59 | 60 | tunnel.hlsl: 61 | ![tunnel](https://user-images.githubusercontent.com/16845654/83958430-67af8080-a826-11ea-878f-9bf0b429d870.png) 62 | 63 | sphere.hlsl: 64 | ![sphere](https://user-images.githubusercontent.com/16845654/83236798-f5191380-a148-11ea-9e33-20f5d9f79d7c.PNG) 65 | 66 | cornellbox.hlsl: 67 | ![cornellbox](https://user-images.githubusercontent.com/16845654/83236801-f77b6d80-a148-11ea-9a4f-574868cca5a9.PNG) 68 | 69 | tron.hlsl: 70 | ![tron](https://user-images.githubusercontent.com/16845654/83236803-f9453100-a148-11ea-86e1-8575461cd551.PNG) 71 | 72 | coolbox.hlsl: 73 | ![coolbox](https://user-images.githubusercontent.com/16845654/83236807-fa765e00-a148-11ea-9dd8-4126e039e73e.PNG) 74 | 75 | interior.hlsl: 76 | ![interior](https://user-images.githubusercontent.com/16845654/83236809-fba78b00-a148-11ea-99c7-1cc9691dae29.PNG) 77 | --------------------------------------------------------------------------------