├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Render Terrain.sln └── Render Terrain ├── BoundingVolume.cpp ├── BoundingVolume.h ├── Camera.cpp ├── Camera.h ├── Common.h ├── D3DX12.h ├── DayNightCycle.cpp ├── DayNightCycle.h ├── DirectionalLight.cpp ├── DirectionalLight.h ├── Frame.cpp ├── Frame.h ├── Graphics.cpp ├── Graphics.h ├── Light.cpp ├── Light.h ├── Main.cpp ├── Material.cpp ├── Material.h ├── Render Terrain.vcxproj ├── Render Terrain.vcxproj.filters ├── RenderShadowMapDS.hlsl ├── RenderShadowMapHS.hlsl ├── RenderTerrain2dPS.hlsl ├── RenderTerrain2dVS.hlsl ├── RenderTerrainTessDS.hlsl ├── RenderTerrainTessHS.hlsl ├── RenderTerrainTessPS.hlsl ├── RenderTerrainTessVS.hlsl ├── ResourceManager.cpp ├── ResourceManager.h ├── Scene.cpp ├── Scene.h ├── Snow.png ├── Terrain.cpp ├── Terrain.h ├── Window.cpp ├── Window.h ├── dirt.png ├── dirtdepthmap.png ├── dirtdiffuse.png ├── dirtnormalmap.png ├── dirtnormals.png ├── displacement.png ├── displacementmap.png ├── displacementmapnormals.png ├── grass.png ├── grassdepthmap.png ├── grassdiffuse.png ├── grassnormalmap.png ├── grassnormals.png ├── heightmap10.png ├── heightmap2.png ├── heightmap3.png ├── heightmap4.png ├── heightmap5.png ├── heightmap6.png ├── heightmap8.png ├── heightmap9.png ├── lodepng.cpp ├── lodepng.h ├── rock.png ├── rockdepthmap.png ├── rockdiffuse.png ├── rocknormalmap.png ├── rocknormals.png ├── snowdepthmap.png ├── snowdiffuse.png ├── snownormalmap.png └── snownormals.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | *.VC.opendb 81 | *.VC.db 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | 88 | # TFS 2012 Local Workspace 89 | $tf/ 90 | 91 | # Guidance Automation Toolkit 92 | *.gpState 93 | 94 | # ReSharper is a .NET coding add-in 95 | _ReSharper*/ 96 | *.[Rr]e[Ss]harper 97 | *.DotSettings.user 98 | 99 | # JustCode is a .NET coding add-in 100 | .JustCode 101 | 102 | # TeamCity is a build add-in 103 | _TeamCity* 104 | 105 | # DotCover is a Code Coverage Tool 106 | *.dotCover 107 | 108 | # NCrunch 109 | _NCrunch_* 110 | .*crunch*.local.xml 111 | 112 | # MightyMoose 113 | *.mm.* 114 | AutoTest.Net/ 115 | 116 | # Web workbench (sass) 117 | .sass-cache/ 118 | 119 | # Installshield output folder 120 | [Ee]xpress/ 121 | 122 | # DocProject is a documentation generator add-in 123 | DocProject/buildhelp/ 124 | DocProject/Help/*.HxT 125 | DocProject/Help/*.HxC 126 | DocProject/Help/*.hhc 127 | DocProject/Help/*.hhk 128 | DocProject/Help/*.hhp 129 | DocProject/Help/Html2 130 | DocProject/Help/html 131 | 132 | # Click-Once directory 133 | publish/ 134 | 135 | # Publish Web Output 136 | *.[Pp]ublish.xml 137 | *.azurePubxml 138 | ## TODO: Comment the next line if you want to checkin your 139 | ## web deploy settings but do note that will include unencrypted 140 | ## passwords 141 | #*.pubxml 142 | 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Windows Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Windows Store app package directory 159 | AppPackages/ 160 | 161 | # Visual Studio cache files 162 | # files ending in .cache can be ignored 163 | *.[Cc]ache 164 | # but keep track of directories ending in .cache 165 | !*.[Cc]ache/ 166 | 167 | # Others 168 | ClientBin/ 169 | [Ss]tyle[Cc]op.* 170 | ~$* 171 | *~ 172 | *.dbmdl 173 | *.dbproj.schemaview 174 | *.pfx 175 | *.publishsettings 176 | node_modules/ 177 | orleans.codegen.cs 178 | 179 | # RIA/Silverlight projects 180 | Generated_Code/ 181 | 182 | # Backup & report files from converting an old project file 183 | # to a newer Visual Studio version. Backup files are not needed, 184 | # because we have git ;-) 185 | _UpgradeReport_Files/ 186 | Backup*/ 187 | UpgradeLog*.XML 188 | UpgradeLog*.htm 189 | 190 | # SQL Server files 191 | *.mdf 192 | *.ldf 193 | 194 | # Business Intelligence projects 195 | *.rdl.data 196 | *.bim.layout 197 | *.bim_*.settings 198 | 199 | # Microsoft Fakes 200 | FakesAssemblies/ 201 | 202 | # Node.js Tools for Visual Studio 203 | .ntvs_analysis.dat 204 | 205 | # Visual Studio 6 build log 206 | *.plg 207 | 208 | # Visual Studio 6 workspace options file 209 | *.opt 210 | 211 | # LightSwitch generated files 212 | GeneratedArtifacts/ 213 | _Pvt_Extensions/ 214 | ModelManifest.xml 215 | /Render Terrain/hmsmall.jpg 216 | /Render Terrain/heightmap.jpg 217 | /Render Terrain/blank window.png 218 | /Render Terrain/hm1thumbnail.png 219 | /Render Terrain/terrain_composite.jpg 220 | /Render Terrain/ObitalStation01-Raw.jpg 221 | /Render Terrain/first3d.jpg 222 | /Render Terrain/heightmap3.jpg 223 | /Render Terrain/heightmap4.jpg 224 | /Render Terrain/heightmap5.jpg 225 | /Render Terrain/heightmap6.jpg 226 | /Render Terrain/Snow.jpg 227 | /Render Terrain/rock.jpg 228 | /Render Terrain/grass.jpg 229 | /Render Terrain/dirt2normalmap.png 230 | /Render Terrain/dirt.jpg 231 | /Render Terrain/detailmap1.png 232 | /Render Terrain/Final.jpg 233 | /Render Terrain/tessfactor4.jpg 234 | /Render Terrain/tessfactor8.jpg 235 | /Render Terrain/shadowsnotdisplaced.jpg 236 | /Render Terrain/shadowsdisplaced.jpg 237 | /Render Terrain/blendlines.jpg 238 | /Render Terrain/slopebased.jpg 239 | /Render Terrain/tightenedtriplanar.jpg 240 | /Render Terrain/triplanar2tex.jpg 241 | /Render Terrain/rock2.jpg 242 | /Render Terrain/weirdissueaftercombining.jpg 243 | /Render Terrain/rtp29featured.png 244 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Render-Terrain 2 | DX12 Terrain Renderer 3 | Author: Chris Serson (aka Traagen) 4 | Date: October 14, 2016 5 | 6 | Description: 7 | This project implements a simple terrain engine in DirectX 12. 8 | It is currently not multi-threaded. 9 | The engine uses a static mesh, centered on the origin, to represent the terrain. 10 | Dynamic Level of Detail is implemented using tessellation. 11 | Adds detail using displacement mapping and bump mapping. 12 | Uses Cascaded Shadow Maps to shadow the entire terrain. 13 | Can be toggled using a simple colour palette and diffuse texture splatting. 14 | 15 | Controls: 16 | Movement is relative to the direction you are currently facing. 17 | Mouse look to rotate camera. 18 | A - strafe left 19 | D - strafe right 20 | W - forward 21 | S - backward 22 | Q - strafe up 23 | Z - strafe down 24 | 25 | 1 - 2D view 26 | 2 - 3D view 27 | T - toggle textures on and off 28 | L - toggle whether the camera is locked to the Terrain 29 | ESC - exit 30 | 31 | File Resources: 32 | All files currently being loaded are PNG files containing RGBA data. 33 | The engine will take an arbitrary greyscale PNG as a height maps. 34 | There are multiple heightmap#.png files included that all work with the engine. 35 | To switch which is being loaded, change the filename in the Scene constructor. 36 | The displacement map contains a normal map in the RGB channels and a height map 37 | in the A channel of the PNG file. 38 | Requires 4 normal maps and 4 diffuse maps be specified for height and slope based 39 | normal and diffuse mapping. 40 | To specify diffuse and normal maps, specify the colour/normal in the RGB channels 41 | and place a greyscale depth/height value in the A channel of the PNG. -------------------------------------------------------------------------------- /Render Terrain.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Render Terrain", "Render Terrain\Render Terrain.vcxproj", "{63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Debug|x64.ActiveCfg = Debug|x64 17 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Debug|x64.Build.0 = Debug|x64 18 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Debug|x86.ActiveCfg = Debug|Win32 19 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Debug|x86.Build.0 = Debug|Win32 20 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Release|x64.ActiveCfg = Release|x64 21 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Release|x64.Build.0 = Release|x64 22 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Release|x86.ActiveCfg = Release|Win32 23 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Render Terrain/BoundingVolume.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | BoundingVolume.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Classes and methods defining bounding volumes. 8 | */ 9 | 10 | #include "BoundingVolume.h" 11 | 12 | BoundingSphere FindBoundingSphere(XMFLOAT3 a, XMFLOAT3 b, XMFLOAT3 c) { 13 | XMVECTOR _a = XMLoadFloat3(&a); 14 | XMVECTOR _b = XMLoadFloat3(&b); 15 | XMVECTOR _c = XMLoadFloat3(&c); 16 | XMVECTOR ac = _c - _a; 17 | XMVECTOR ab = _b - _a; 18 | XMVECTOR N = XMVector3Normalize(XMVector3Cross(ab, ac)); 19 | XMVECTOR halfAB = _a + ab * 0.5f; 20 | XMVECTOR halfAC = _a + ac * 0.5f; 21 | XMVECTOR perpAB = XMVector3Normalize(XMVector3Cross(ab, N)); 22 | XMVECTOR perpAC = XMVector3Normalize(XMVector3Cross(ac, N)); 23 | // line,line intersection test. Line 1 origin: halfAB, direction: perpAB; Line 2 origin: halfAC, direction: perpAC 24 | N = XMVector3Cross(perpAB, perpAC); 25 | XMVECTOR SR = halfAB - halfAC; 26 | XMFLOAT4 _N, _SR, _E; 27 | XMStoreFloat4(&_N, N); 28 | XMStoreFloat4(&_SR, SR); 29 | XMStoreFloat4(&_E, perpAC); 30 | float absX = fabsf(_N.x); 31 | float absY = fabsf(_N.y); 32 | float absZ = fabsf(_N.z); 33 | float t; 34 | if (absZ > absX && absZ > absY) { 35 | t = (_SR.x * _E.y - _SR.y * _E.x) / _N.z; 36 | } 37 | else if (absX > absY) { 38 | t = (_SR.y * _E.z - _SR.z * _E.y) / _N.x; 39 | } 40 | else { 41 | t = (_SR.z * _E.x - _SR.x * _E.z) / _N.y; 42 | } 43 | 44 | XMVECTOR Circumcenter = halfAB - t * perpAB; 45 | XMVECTOR r = XMVector3Length(_c - Circumcenter); 46 | 47 | float radius; 48 | XMFLOAT3 center; 49 | XMStoreFloat(&radius, r); 50 | XMStoreFloat3(¢er, Circumcenter); 51 | return BoundingSphere(radius, center); 52 | } -------------------------------------------------------------------------------- /Render Terrain/BoundingVolume.h: -------------------------------------------------------------------------------- 1 | /* 2 | BoundingVolume.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Classes and methods defining bounding volumes. Currently only BoundingSphere. 8 | 9 | Usage: - Proper shutdown is handled by the destructor. 10 | 11 | Future Work: - Add collision detection to Bounding Sphere. 12 | - Add Axis Aligned Bounding Box. 13 | - Add Object Oriented Bounding Box. 14 | - Add K-DOP. 15 | */ 16 | #pragma once 17 | #include 18 | 19 | using namespace DirectX; 20 | 21 | class BoundingSphere; 22 | 23 | // Find a bounding sphere by finding the circumcenter of 3 points and the distance from the points to the circumcenter 24 | BoundingSphere FindBoundingSphere(XMFLOAT3 a, XMFLOAT3 b, XMFLOAT3 c); 25 | 26 | class BoundingSphere { 27 | public: 28 | BoundingSphere(float r = 0.0f, XMFLOAT3 c = XMFLOAT3(0.0f, 0.0f, 0.0f)) : m_valRadius(r), m_vCenter(c) {} 29 | ~BoundingSphere() {} 30 | 31 | float GetRadius() { return m_valRadius; } 32 | void SetRadius(float r) { m_valRadius = r; } 33 | 34 | XMFLOAT3 GetCenter() { return m_vCenter; } 35 | void SetCenter(XMFLOAT3 c) { m_vCenter = c; } 36 | void SetCenter(float x, float y, float z) { m_vCenter = XMFLOAT3(x, y, z); } 37 | 38 | private: 39 | float m_valRadius; 40 | XMFLOAT3 m_vCenter; 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /Render Terrain/Camera.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Camera.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for creating and controlling the camera 8 | */ 9 | 10 | #include "Camera.h" 11 | 12 | Camera::Camera(int h, int w) { 13 | m_angleYaw = m_anglePitch = m_angleRoll = 0.0f; 14 | m_wScreen = w; 15 | m_hScreen = h; 16 | m_fovVertical = 60.0f; 17 | double tmp = atan(tan(XMConvertToRadians(m_fovVertical) * 0.5) * w / h) * 2.0; 18 | m_fovHorizontal = XMConvertToDegrees((float)tmp); 19 | 20 | // build projection matrix 21 | XMMATRIX proj = XMMatrixPerspectiveFovLH(XMConvertToRadians(m_fovVertical), (float)w / (float)h, 0.1f, 3000.0f); 22 | XMStoreFloat4x4(&m_mProjection, proj); 23 | 24 | // set starting camera state 25 | m_vPos = XMFLOAT4(0.0f, 0.0f, 150.0f, 0.0f); 26 | XMVECTOR look = XMVector3Normalize(XMLoadFloat4(&XMFLOAT4(1.0f, 1.0f, 0.0f, 0.0f))); 27 | XMStoreFloat4(&m_vStartLook, look); 28 | XMVECTOR left = XMVector3Cross(look, XMLoadFloat4(&XMFLOAT4(0.0f, 0.0f, 1.0f, 0.0f))); 29 | XMStoreFloat4(&m_vStartLeft, left); 30 | XMVECTOR up = XMVector3Cross(left, look); 31 | XMStoreFloat4(&m_vStartUp, up); 32 | 33 | Update(); 34 | } 35 | 36 | Camera::~Camera() { 37 | } 38 | 39 | // combine the view and projection matrices and transpose the result 40 | XMFLOAT4X4 Camera::GetViewProjectionMatrixTransposed() { 41 | XMMATRIX view = XMLoadFloat4x4(&m_mView); 42 | XMMATRIX proj = XMLoadFloat4x4(&m_mProjection); 43 | XMMATRIX viewproj = XMMatrixTranspose(view * proj); 44 | XMFLOAT4X4 final; 45 | XMStoreFloat4x4(&final, viewproj); 46 | return final; 47 | } 48 | 49 | // Return the 6 planes forming the view frustum. Stored in the array planes. 50 | void Camera::GetViewFrustum(XMFLOAT4 planes[6]) { 51 | XMMATRIX view = XMLoadFloat4x4(&m_mView); 52 | XMMATRIX proj = XMLoadFloat4x4(&m_mProjection); 53 | XMFLOAT4X4 M; 54 | XMStoreFloat4x4(&M, view * proj); 55 | 56 | // left 57 | planes[0].x = M(0, 3) + M(0, 0); 58 | planes[0].y = M(1, 3) + M(1, 0); 59 | planes[0].z = M(2, 3) + M(2, 0); 60 | planes[0].w = M(3, 3) + M(3, 0); 61 | 62 | // right 63 | planes[1].x = M(0, 3) - M(0, 0); 64 | planes[1].y = M(1, 3) - M(1, 0); 65 | planes[1].z = M(2, 3) - M(2, 0); 66 | planes[1].w = M(3, 3) - M(3, 0); 67 | 68 | // bottom 69 | planes[2].x = M(0, 3) + M(0, 1); 70 | planes[2].y = M(1, 3) + M(1, 1); 71 | planes[2].z = M(2, 3) + M(2, 1); 72 | planes[2].w = M(3, 3) + M(3, 1); 73 | 74 | // top 75 | planes[3].x = M(0, 3) - M(0, 1); 76 | planes[3].y = M(1, 3) - M(1, 1); 77 | planes[3].z = M(2, 3) - M(2, 1); 78 | planes[3].w = M(3, 3) - M(3, 1); 79 | 80 | // near 81 | planes[4].x = M(0, 3) + M(0, 2); 82 | planes[4].y = M(1, 3) + M(1, 2); 83 | planes[4].z = M(2, 3) + M(2, 2); 84 | planes[4].w = M(3, 3) + M(3, 2); 85 | 86 | // far 87 | planes[5].x = M(0, 3) - M(0, 2); 88 | planes[5].y = M(1, 3) - M(1, 2); 89 | planes[5].z = M(2, 3) - M(2, 2); 90 | planes[5].w = M(3, 3) - M(3, 2); 91 | 92 | // normalize all planes 93 | for (auto i = 0; i < 6; ++i) { 94 | XMVECTOR v = XMPlaneNormalize(XMLoadFloat4(&planes[i])); 95 | XMStoreFloat4(&planes[i], v); 96 | } 97 | } 98 | 99 | // find a view frustum based on the current view matrix and the provided near and far planes. 100 | Frustum Camera::CalculateFrustumByNearFar(float near, float far) { 101 | Frustum f; 102 | 103 | float tanHalfHFOV = tanf(XMConvertToRadians(m_fovHorizontal / 2.0f)); 104 | float tanHalfVFOV = tanf(XMConvertToRadians(m_fovVertical / 2.0f)); 105 | 106 | float xNear = near * tanHalfHFOV; 107 | float xFar = far * tanHalfHFOV; 108 | float yNear = near * tanHalfVFOV; 109 | float yFar = far * tanHalfVFOV; 110 | 111 | f.nlb = XMFLOAT3(-xNear, -yNear, near); 112 | f.nrb = XMFLOAT3( xNear, -yNear, near); 113 | f.nlt = XMFLOAT3(-xNear, yNear, near); 114 | f.nrt = XMFLOAT3( xNear, yNear, near); 115 | 116 | f.flb = XMFLOAT3(-xFar, -yFar, far); 117 | f.frb = XMFLOAT3( xFar, -yFar, far); 118 | f.flt = XMFLOAT3(-xFar, yFar, far); 119 | f.frt = XMFLOAT3( xFar, yFar, far); 120 | 121 | // get the current view and projection matrices 122 | XMMATRIX view = XMLoadFloat4x4(&m_mView); 123 | XMMATRIX viewproj = view; 124 | XMMATRIX invViewProj = XMMatrixInverse(nullptr, viewproj); // the inverse view/projection matrix 125 | 126 | XMVECTOR nlb = XMLoadFloat3(&f.nlb); 127 | XMVECTOR nrb = XMLoadFloat3(&f.nrb); 128 | XMVECTOR nlt = XMLoadFloat3(&f.nlt); 129 | XMVECTOR nrt = XMLoadFloat3(&f.nrt); 130 | XMVECTOR flb = XMLoadFloat3(&f.flb); 131 | XMVECTOR frb = XMLoadFloat3(&f.frb); 132 | XMVECTOR flt = XMLoadFloat3(&f.flt); 133 | XMVECTOR frt = XMLoadFloat3(&f.frt); 134 | 135 | nlb = XMVector3Transform(nlb, invViewProj); 136 | nrb = XMVector3Transform(nrb, invViewProj); 137 | nlt = XMVector3Transform(nlt, invViewProj); 138 | nrt = XMVector3Transform(nrt, invViewProj); 139 | flb = XMVector3Transform(flb, invViewProj); 140 | frb = XMVector3Transform(frb, invViewProj); 141 | flt = XMVector3Transform(flt, invViewProj); 142 | frt = XMVector3Transform(frt, invViewProj); 143 | 144 | XMFLOAT4 _nlb, _nrb, _nrt, _nlt, _flb, _frt, _frb, _flt; 145 | XMStoreFloat4(&_nlb, nlb); 146 | XMStoreFloat4(&_nrb, nrb); 147 | XMStoreFloat4(&_nlt, nlt); 148 | XMStoreFloat4(&_nrt, nrt); 149 | XMStoreFloat4(&_flb, flb); 150 | XMStoreFloat4(&_frb, frb); 151 | XMStoreFloat4(&_flt, flt); 152 | XMStoreFloat4(&_frt, frt); 153 | 154 | nlb = nlb / _nlb.w; 155 | nrb = nrb / _nrb.w; 156 | nlt = nlt / _nlt.w; 157 | nrt = nrt / _nrt.w; 158 | flb = flb / _flb.w; 159 | frb = frb / _frb.w; 160 | flt = flt / _flt.w; 161 | frt = frt / _frt.w; 162 | 163 | XMStoreFloat3(&f.nlb, nlb); 164 | XMStoreFloat3(&f.nrb, nrb); 165 | XMStoreFloat3(&f.nlt, nlt); 166 | XMStoreFloat3(&f.nrt, nrt); 167 | XMStoreFloat3(&f.flb, flb); 168 | XMStoreFloat3(&f.frb, frb); 169 | XMStoreFloat3(&f.flt, flt); 170 | XMStoreFloat3(&f.frt, frt); 171 | 172 | f.bs = FindBoundingSphere(f.nlb, f.flb, f.frt); 173 | 174 | return f; 175 | } 176 | 177 | // Move the camera along its 3 axis: m_vStartLook (forward/backward), m_vStartLeft (left/left), m_vStartUp (up/down) 178 | void Camera::Translate(XMFLOAT3 move) { 179 | XMVECTOR look = XMLoadFloat4(&m_vCurLook); 180 | XMVECTOR left = XMLoadFloat4(&m_vCurLeft); 181 | XMVECTOR up = XMLoadFloat4(&m_vCurUp); 182 | XMVECTOR tmp = XMLoadFloat4(&m_vPos); 183 | 184 | tmp += look * move.x + left * move.y + up * move.z; 185 | 186 | XMStoreFloat4(&m_vPos, tmp); 187 | 188 | Update(); 189 | } 190 | 191 | // rotate the camera up and down, around m_vStartLeft 192 | void Camera::Pitch(float theta) { 193 | m_anglePitch += theta; 194 | m_anglePitch = m_anglePitch > 360 ? m_anglePitch - 360 : m_anglePitch < -360 ? m_anglePitch + 360 : m_anglePitch; 195 | 196 | Update(); 197 | } 198 | 199 | // rotate the camera left and right, around m_vStartUp 200 | void Camera::Yaw(float theta) { 201 | m_angleYaw += theta; 202 | m_angleYaw = m_angleYaw > 360 ? m_angleYaw - 360 : m_angleYaw < -360 ? m_angleYaw + 360 : m_angleYaw; 203 | 204 | Update(); 205 | } 206 | 207 | // rotate the camera clockwise and counter-clockwise, around m_vStartLook 208 | void Camera::Roll(float theta) { 209 | m_angleRoll += theta; 210 | m_angleRoll = m_angleRoll > 360 ? m_angleRoll - 360 : m_angleRoll < -360 ? m_angleRoll + 360 : m_angleRoll; 211 | 212 | Update(); 213 | } 214 | 215 | // Lock the Camera's eye to the supplied position. 216 | void Camera::LockPosition(XMFLOAT4 p) { 217 | m_vPos = p; 218 | 219 | Update(); 220 | } 221 | 222 | void Camera::Update() { 223 | // rotate camera based on yaw, pitch, and roll. 224 | XMVECTOR look = XMLoadFloat4(&m_vStartLook); 225 | XMVECTOR up = XMLoadFloat4(&m_vStartUp); 226 | 227 | float pitch_rad = XMConvertToRadians(m_anglePitch); 228 | float yaw_rad = XMConvertToRadians(m_angleYaw); 229 | float roll_rad = XMConvertToRadians(m_angleRoll); 230 | 231 | XMMATRIX rot, rotp, roty, rotr; 232 | XMVECTOR left = XMLoadFloat4(&m_vStartLeft); 233 | 234 | rotp = XMMatrixRotationAxis(left, pitch_rad); 235 | roty = XMMatrixRotationAxis(up, yaw_rad); 236 | rotr = XMMatrixRotationAxis(look, roll_rad); 237 | rot = rotp * roty * rotr; 238 | look = XMVector3Normalize(XMVector3Transform(look, rot)); 239 | left = XMVector3Normalize(XMVector3Transform(left, rot)); 240 | up = XMVector3Cross(left, look); 241 | 242 | XMStoreFloat4(&m_vCurLook, look); 243 | XMStoreFloat4(&m_vCurUp, up); 244 | XMStoreFloat4(&m_vCurLeft, left); 245 | 246 | // build view matrix 247 | XMVECTOR camera = XMLoadFloat4(&m_vPos); 248 | XMVECTOR target = camera + look; // add camera position plus target direction to get target location for view matrix function 249 | XMMATRIX view = XMMatrixLookAtLH(camera, target, up); 250 | XMStoreFloat4x4(&m_mView, view); 251 | } 252 | 253 | -------------------------------------------------------------------------------- /Render Terrain/Camera.h: -------------------------------------------------------------------------------- 1 | /* 2 | Camera.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for creating and controlling the camera 8 | 9 | Usage: - Calling the constructor, either through Camera C(...); 10 | or Camera* C; C = new Camera(...);, will initialize 11 | the scene. 12 | - Proper shutdown is handled by the destructor. 13 | - Is hard-coded for DirectXMath 14 | - Translate() to move camera 15 | - Roll(), Pitch(), and Yaw() to rotate camera 16 | 17 | Future Work: - I'm not 100% certain everything is correct when Roll is used. Will need to test further. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "BoundingVolume.h" 24 | 25 | using namespace DirectX; 26 | 27 | struct Frustum { 28 | XMFLOAT3 nlb; 29 | XMFLOAT3 nlt; 30 | XMFLOAT3 nrb; 31 | XMFLOAT3 nrt; 32 | XMFLOAT3 flb; 33 | XMFLOAT3 flt; 34 | XMFLOAT3 frb; 35 | XMFLOAT3 frt; 36 | BoundingSphere bs; 37 | }; 38 | 39 | class Camera 40 | { 41 | public: 42 | Camera(int h, int w); 43 | ~Camera(); 44 | 45 | // combine the view and projection matrices and transpose the result 46 | XMFLOAT4X4 GetViewProjectionMatrixTransposed(); 47 | // returns m_vPos; 48 | XMFLOAT4 GetEyePosition() { return m_vPos; } 49 | // Return the 6 planes forming the view frustum. Stored in the array planes. 50 | void GetViewFrustum(XMFLOAT4 planes[6]); 51 | // Move the camera along its 3 axis: m_vStartLook (forward/backward), m_vStartLeft (left/right), m_vStartUp (up/down) 52 | void Translate(XMFLOAT3 move); 53 | // rotate the camera up and down, around m_vStartLeft 54 | void Pitch(float theta); 55 | // rotate the camera left and right, around m_vStartUp 56 | void Yaw(float theta); 57 | // rotate the camera clockwise and counter-clockwise, around m_vStartLook 58 | void Roll(float theta); 59 | // find a view frustum based on the current view matrix and the provided near and far planes. 60 | Frustum CalculateFrustumByNearFar(float near, float far); 61 | // Lock the Camera's eye to the supplied position. 62 | void LockPosition(XMFLOAT4 p); 63 | 64 | private: 65 | void Update(); 66 | 67 | XMFLOAT4X4 m_mProjection; // Projection matrix 68 | XMFLOAT4X4 m_mView; // View matrix 69 | XMFLOAT4 m_vPos; // Camera position 70 | XMFLOAT4 m_vStartLook; // Starting lookat vector 71 | XMFLOAT4 m_vStartUp; // Starting up vector 72 | XMFLOAT4 m_vStartLeft; // Starting left vector 73 | XMFLOAT4 m_vCurLook; // Current lookat vector 74 | XMFLOAT4 m_vCurUp; // Current up vector 75 | XMFLOAT4 m_vCurLeft; // Current left vector 76 | int m_wScreen; 77 | int m_hScreen; 78 | float m_fovHorizontal; 79 | float m_fovVertical; 80 | float m_angleYaw; 81 | float m_anglePitch; 82 | float m_angleRoll; 83 | }; 84 | 85 | -------------------------------------------------------------------------------- /Render Terrain/Common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Common.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Common constants, functions, etc. 8 | */ 9 | #pragma once 10 | 11 | // linearly interpolate between a and b by amount t. 12 | inline float lerp(float a, float b, float t) { 13 | return a + t * (b - a); 14 | } 15 | 16 | // bilinear interpolation between four values. 17 | // performed as linear interpolation between a and b by u, 18 | // then between c and d by u, 19 | // then between ab and cd by v. 20 | inline float bilerp(float a, float b, float c, float d, float u, float v) { 21 | return lerp(lerp(a, b, u), lerp(c, d, u), v); 22 | } -------------------------------------------------------------------------------- /Render Terrain/DayNightCycle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | DayNightCycle.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for managing the Day/Night Cycle for the scene. 8 | */ 9 | #include "DayNightCycle.h" 10 | 11 | XMFLOAT4 ColorLerp(XMFLOAT4 color1, XMFLOAT4 color2, float interpolator) { 12 | //x + s(y - x) 13 | XMVECTOR c1 = XMLoadFloat4(&color1); 14 | XMVECTOR c2 = XMLoadFloat4(&color2); 15 | XMFLOAT4 newcolor; 16 | XMStoreFloat4(&newcolor, c1 + interpolator * (c2 - c1)); 17 | 18 | return newcolor; 19 | } 20 | 21 | DayNightCycle::DayNightCycle(UINT period, UINT shadowSize) : m_dlSun(XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f), SUN_DIFFUSE_COLORS[0], SUN_SPECULAR_COLORS[0], XMFLOAT3(0.0f, 0.0f, 1.0f)), 22 | m_dlMoon(XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f), XMFLOAT4(0.4f, 0.4f, 0.4f, 1.0f), XMFLOAT4(0.6f, 0.6f, 0.6f, 1.0f), XMFLOAT3(0.0f, 0.0f, -1.0f)), 23 | m_Period(period), m_sizeShadowMap(shadowSize) { 24 | m_tLast = system_clock::now(); 25 | } 26 | 27 | DayNightCycle::~DayNightCycle() { 28 | } 29 | 30 | void DayNightCycle::Update(BoundingSphere& bsScene, Camera* cam) { 31 | time_point now = system_clock::now(); 32 | 33 | if (!m_isPaused) { 34 | // get the amount of time in ms since the last time we updated. 35 | milliseconds elapsed = duration_cast(now - m_tLast); 36 | 37 | // calculate how far to rotate. 38 | double angletorotate = elapsed.count() * m_Period * DEG_PER_MILLI; 39 | float angleinrads = XMConvertToRadians((float)angletorotate); 40 | 41 | // rotate the sun's direction vector. 42 | XMFLOAT3 tmp = m_dlSun.GetLight().direction; 43 | XMVECTOR dir = XMLoadFloat3(&tmp); 44 | XMVECTOR rot = XMQuaternionRotationRollPitchYaw(0.0f, -(float)angleinrads, 0.0f); 45 | dir = XMVector3Normalize(XMVector3Rotate(dir, rot)); 46 | XMStoreFloat3(&tmp, dir); 47 | m_dlSun.SetLightDirection(tmp); 48 | 49 | // use the angletorotate to calculate what the current colour of the Sun should be. 50 | float newangle = fmod(m_angleSun + (float)angletorotate, 360.0f); 51 | int iColor1, iColor2; // color indices to get colors to interpolate between 52 | float iInterpolator; // amount to interpolate by 53 | if (newangle >= 330.0f) { 54 | iColor1 = 11; 55 | iColor2 = 0; 56 | iInterpolator = (newangle - 330.0f) / 30.0f; 57 | } else if (newangle >= 300.0f) { 58 | iColor1 = 10; 59 | iColor2 = 11; 60 | iInterpolator = (newangle - 300.0f) / 30.0f; 61 | } else if (newangle >= 270.0f) { 62 | iColor1 = 9; 63 | iColor2 = 10; 64 | iInterpolator = (newangle - 270.0f) / 30.0f; 65 | } else if (newangle >= 240.0f) { 66 | iColor1 = 8; 67 | iColor2 = 9; 68 | iInterpolator = (newangle - 240.0f) / 30.0f; 69 | } else if (newangle >= 210.0f) { 70 | iColor1 = 7; 71 | iColor2 = 8; 72 | iInterpolator = (newangle - 210.0f) / 30.0f; 73 | } else if (newangle >= 180.0f) { 74 | iColor1 = 6; 75 | iColor2 = 7; 76 | iInterpolator = (newangle - 180.0f) / 30.0f; 77 | } else if (newangle >= 150.0f) { 78 | iColor1 = 5; 79 | iColor2 = 6; 80 | iInterpolator = (newangle - 150.0f) / 30.0f; 81 | } else if (newangle >= 120.0f) { 82 | iColor1 = 4; 83 | iColor2 = 5; 84 | iInterpolator = (newangle - 120.0f) / 30.0f; 85 | } else if (newangle >= 90.0f) { 86 | iColor1 = 3; 87 | iColor2 = 4; 88 | iInterpolator = (newangle - 90.0f) / 30.0f; 89 | } else if (newangle >= 60.0f) { 90 | iColor1 = 2; 91 | iColor2 = 3; 92 | iInterpolator = (newangle - 60.0f) / 30.0f; 93 | } else if (newangle >= 30.0f) { 94 | iColor1 = 1; 95 | iColor2 = 2; 96 | iInterpolator = (newangle - 30.0f) / 30.0f; 97 | } else if (newangle >= 0.0f) { 98 | iColor1 = 0; 99 | iColor2 = 1; 100 | iInterpolator = newangle / 30.0f; 101 | } 102 | 103 | m_dlSun.SetDiffuseColor(ColorLerp(SUN_DIFFUSE_COLORS[iColor1], SUN_DIFFUSE_COLORS[iColor2], iInterpolator)); 104 | m_dlSun.SetSpecularColor(ColorLerp(SUN_SPECULAR_COLORS[iColor1], SUN_SPECULAR_COLORS[iColor2], iInterpolator)); 105 | 106 | m_angleSun = newangle; 107 | } 108 | 109 | // update the time for the next pass. 110 | m_tLast = now; 111 | 112 | CalculateShadowMatrices(bsScene, cam); 113 | } 114 | 115 | void DayNightCycle::CalculateShadowMatrices(BoundingSphere& bsScene, Camera* cam) { 116 | LightSource light = m_dlSun.GetLight(); 117 | XMVECTOR lightdir = XMLoadFloat3(&light.direction); 118 | XMFLOAT3 vCenterScene = bsScene.GetCenter(); 119 | XMVECTOR targetpos = XMLoadFloat3(&vCenterScene); 120 | 121 | float offset = (float)(m_sizeShadowMap + 8) / (float)m_sizeShadowMap; // add padding to projection for rounding and for pcf. 122 | float radiusScene = ceilf(bsScene.GetRadius()) * offset; 123 | 124 | XMVECTOR lightpos = targetpos - 2.0f * radiusScene * lightdir; 125 | XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); 126 | up = XMVector3Cross(up, lightdir); 127 | 128 | XMMATRIX V = XMMatrixLookAtLH(lightpos, targetpos, up); // light space view matrix transform bounding sphere to light space 129 | XMFLOAT4 spherecenterls; 130 | 131 | // create the cascades. 132 | for (int i = 0; i < 3; ++i) { 133 | Frustum fCascade = cam->CalculateFrustumByNearFar(CASCADE_PLANES[i], CASCADE_PLANES[i + 1]); 134 | float radius = ceilf(fCascade.bs.GetRadius()); 135 | radius *= offset; 136 | XMVECTOR c = XMLoadFloat3(&fCascade.bs.GetCenter()); 137 | XMStoreFloat4(&spherecenterls, XMVector3TransformCoord(c, V)); 138 | XMVECTOR sc = XMLoadFloat3(&vCenterScene); 139 | XMFLOAT4 cbs; 140 | XMStoreFloat4(&cbs, XMVector3TransformCoord(sc, V)); 141 | 142 | // orthographic frustum 143 | float l = spherecenterls.x - radius; 144 | float b = spherecenterls.y - radius; 145 | float n = spherecenterls.z - cbs.z - radiusScene; 146 | float r = spherecenterls.x + radius; 147 | float t = spherecenterls.y + radius; 148 | float f = spherecenterls.z + cbs.z + radiusScene; 149 | 150 | XMMATRIX P = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f); 151 | 152 | XMMATRIX S = V * P; 153 | 154 | // add rounding to update shadowmap by texel-sized increments. 155 | XMVECTOR shadowOrigin = XMVector3Transform(XMVectorZero(), S); 156 | shadowOrigin *= ((float)(m_sizeShadowMap + offset) / 4.0f); 157 | XMFLOAT2 so; 158 | XMStoreFloat2(&so, shadowOrigin); 159 | XMVECTOR roundedOrigin = XMLoadFloat2(&XMFLOAT2(round(so.x), round(so.y))); 160 | XMVECTOR rounding = roundedOrigin - shadowOrigin; 161 | rounding /= ((m_sizeShadowMap + offset) / 4.0f); 162 | XMStoreFloat2(&so, rounding); 163 | XMMATRIX roundMatrix = XMMatrixTranslation(so.x, so.y, 0.0f); 164 | S *= roundMatrix; 165 | 166 | // Calculate the frustum planes for this view projection matrix. 167 | CalculateShadowFrustum(i, S); 168 | 169 | XMStoreFloat4x4(&m_amShadowViewProjs[i], XMMatrixTranspose(S)); 170 | 171 | // transform NDC space [-1, +1]^2 to texture space [0, 1]^2 172 | float x, y; 173 | if (i == 0) { 174 | x = 0.25f; 175 | y = 0.25f; 176 | } else if (i == 1) { 177 | x = 0.25f; 178 | y = 0.75f; 179 | } else if (i == 2) { 180 | x = 0.75f; 181 | y = 0.25f; 182 | } else { 183 | x = 0.75f; 184 | y = 0.75f; 185 | } 186 | 187 | XMMATRIX T(0.25f, 0.0f, 0.0f, 0.0f, 188 | 0.0f, -0.25f, 0.0f, 0.0f, 189 | 0.0f, 0.0f, 1.0f, 0.0f, 190 | x, y, 0.0f, 1.0f); 191 | 192 | S *= T; 193 | 194 | XMStoreFloat4x4(&m_amShadowViewProjTexs[i], XMMatrixTranspose(S)); 195 | } 196 | 197 | XMVECTOR c = XMLoadFloat3(&vCenterScene); 198 | XMStoreFloat4(&spherecenterls, XMVector3TransformCoord(c, V)); 199 | 200 | // orthographic frustum 201 | float l = spherecenterls.x - radiusScene; 202 | float b = spherecenterls.y - radiusScene; 203 | float n = spherecenterls.z - radiusScene; 204 | float r = spherecenterls.x + radiusScene; 205 | float t = spherecenterls.y + radiusScene; 206 | float f = spherecenterls.z + radiusScene; 207 | 208 | XMMATRIX P = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f); 209 | 210 | XMMATRIX S = V * P; 211 | 212 | // add rounding to update shadowmap by texel-sized increments. 213 | XMVECTOR shadowOrigin = XMVector3Transform(XMVectorZero(), S); 214 | shadowOrigin *= ((float)(m_sizeShadowMap + offset) / 4.0f); 215 | XMFLOAT2 so; 216 | XMStoreFloat2(&so, shadowOrigin); 217 | XMVECTOR roundedOrigin = XMLoadFloat2(&XMFLOAT2(round(so.x), round(so.y))); 218 | XMVECTOR rounding = roundedOrigin - shadowOrigin; 219 | rounding /= ((m_sizeShadowMap + offset) / 4.0f); 220 | XMStoreFloat2(&so, rounding); 221 | XMMATRIX roundMatrix = XMMatrixTranslation(so.x, so.y, 0.0f); 222 | S *= roundMatrix; 223 | 224 | // Calculate the frustum planes for this view projection matrix. 225 | CalculateShadowFrustum(3, S); 226 | 227 | XMStoreFloat4x4(&m_amShadowViewProjs[3], XMMatrixTranspose(S)); 228 | 229 | // transform NDC space [-1, +1]^2 to texture space [0, 1]^2 230 | XMMATRIX T(0.25f, 0.0f, 0.0f, 0.0f, 231 | 0.0f, -0.25f, 0.0f, 0.0f, 232 | 0.0f, 0.0f, 1.0f, 0.0f, 233 | 0.75f, 0.75f, 0.0f, 1.0f); 234 | 235 | S *= T; 236 | 237 | XMStoreFloat4x4(&m_amShadowViewProjTexs[3], XMMatrixTranspose(S)); 238 | } 239 | 240 | void DayNightCycle::CalculateShadowFrustum(int i, XMMATRIX VP) { 241 | XMFLOAT4X4 M; 242 | XMStoreFloat4x4(&M, VP); 243 | 244 | // left 245 | m_aShadowFrustums[i][0].x = M(0, 3) + M(0, 0); 246 | m_aShadowFrustums[i][0].y = M(1, 3) + M(1, 0); 247 | m_aShadowFrustums[i][0].z = M(2, 3) + M(2, 0); 248 | m_aShadowFrustums[i][0].w = M(3, 3) + M(3, 0); 249 | 250 | // right 251 | m_aShadowFrustums[i][1].x = M(0, 3) - M(0, 0); 252 | m_aShadowFrustums[i][1].y = M(1, 3) - M(1, 0); 253 | m_aShadowFrustums[i][1].z = M(2, 3) - M(2, 0); 254 | m_aShadowFrustums[i][1].w = M(3, 3) - M(3, 0); 255 | 256 | // bottom 257 | m_aShadowFrustums[i][2].x = M(0, 3) + M(0, 1); 258 | m_aShadowFrustums[i][2].y = M(1, 3) + M(1, 1); 259 | m_aShadowFrustums[i][2].z = M(2, 3) + M(2, 1); 260 | m_aShadowFrustums[i][2].w = M(3, 3) + M(3, 1); 261 | 262 | // top 263 | m_aShadowFrustums[i][3].x = M(0, 3) - M(0, 1); 264 | m_aShadowFrustums[i][3].y = M(1, 3) - M(1, 1); 265 | m_aShadowFrustums[i][3].z = M(2, 3) - M(2, 1); 266 | m_aShadowFrustums[i][3].w = M(3, 3) - M(3, 1); 267 | 268 | // normalize all planes 269 | for (auto j = 0; j < 4; ++j) { 270 | XMVECTOR v = XMPlaneNormalize(XMLoadFloat4(&m_aShadowFrustums[i][j])); 271 | XMStoreFloat4(&m_aShadowFrustums[i][j], v); 272 | } 273 | } 274 | 275 | void DayNightCycle::GetShadowFrustum(int i, XMFLOAT4 planes[6]) { 276 | for (int j = 0; j < 4; ++j) { 277 | planes[j] = m_aShadowFrustums[i][j]; 278 | } 279 | } -------------------------------------------------------------------------------- /Render Terrain/DayNightCycle.h: -------------------------------------------------------------------------------- 1 | /* 2 | DayNightCycle.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for managing the Day/Night Cycle for the scene. 8 | 9 | Usage: - Calling the constructor, either through DayNightCycle D(...); 10 | or DayNightCycle* D; D = new DayNightCycle(...);, will initialize 11 | the object. 12 | - Proper shutdown is handled by the destructor. 13 | - Call Update() to move time forward. Moves forward by real time passed * m_Period. 14 | - Currently only works for Sun, aligned with y axis. 15 | - Time currently starts at midnight 16 | - Diffuse and Specular light intensities for the Sun now interpolated based on angle/position of Sun. 17 | 18 | Future Work: - Add support for both Sun and Moon at arbitrary locations. 19 | - Add support for animated sky box depicting time of day. 20 | */ 21 | #pragma once 22 | 23 | #include "DirectionalLight.h" 24 | #include "Camera.h" 25 | #include 26 | 27 | using namespace std::chrono; 28 | 29 | static const double DEG_PER_MILLI = 360.0 / 86400000.0; // the number of degrees per millisecond, assuming you rotate 360 degrees in 24 hours. 30 | static const XMFLOAT4 SUN_DIFFUSE_COLORS[] = { 31 | { 0.0f, 0.0f, 0.0f, 1.0f }, 32 | { 0.0f, 0.0f, 0.0f, 1.0f }, 33 | { 0.0f, 0.0f, 0.0f, 1.0f }, 34 | { 0.9f, 0.2f, 0.2f, 1.0f }, 35 | { 0.98f, 0.86f, 0.2f, 1.0f }, 36 | { 0.8f, 0.8f, 0.6f, 1.0f }, 37 | { 0.8f, 0.8f, 0.8f, 1.0f }, 38 | { 0.8f, 0.8f, 0.6f, 1.0f }, 39 | { 0.98f, 0.86f, 0.2f, 1.0f}, 40 | { 0.9f, 0.2f, 0.2f, 1.0f }, 41 | { 0.0f, 0.0f, 0.0f, 1.0f }, 42 | { 0.0f, 0.0f, 0.0f, 1.0f } 43 | }; 44 | static const XMFLOAT4 SUN_SPECULAR_COLORS[] = { 45 | { 0.0f, 0.0f, 0.0f, 1.0f }, 46 | { 0.0f, 0.0f, 0.0f, 1.0f }, 47 | { 0.0f, 0.0f, 0.0f, 1.0f }, 48 | { 0.5f, 0.5f, 0.5f, 1.0f }, 49 | { 0.8f, 0.8f, 0.8f, 1.0f }, 50 | { 1.0f, 1.0f, 1.0f, 1.0f }, 51 | { 1.0f, 1.0f, 1.0f, 1.0f }, 52 | { 1.0f, 1.0f, 1.0f, 1.0f }, 53 | { 0.8f, 0.8f, 0.8f, 1.0f }, 54 | { 0.5f, 0.5f, 0.5f, 1.0f }, 55 | { 0.0f, 0.0f, 0.0f, 1.0f }, 56 | { 0.0f, 0.0f, 0.0f, 1.0f } 57 | }; 58 | static const float CASCADE_PLANES[] = { 0.1f, 64.0f, 128.0f, 256.0f, 1200.0f }; 59 | 60 | class DayNightCycle { 61 | public: 62 | DayNightCycle(UINT period, UINT shadowSize); 63 | ~DayNightCycle(); 64 | 65 | void Update(BoundingSphere& bsScene, Camera* cam); 66 | void TogglePause() { m_isPaused = !m_isPaused; } 67 | 68 | LightSource GetLight() { return m_dlSun.GetLight(); } 69 | XMFLOAT4X4 GetShadowViewProjMatrix(int i) { return m_amShadowViewProjs[i]; } 70 | XMFLOAT4X4 GetShadowViewProjTexMatrix(int i) { return m_amShadowViewProjTexs[i]; } 71 | void GetShadowFrustum(int i, XMFLOAT4 planes[6]); 72 | 73 | private: 74 | void CalculateShadowMatrices(BoundingSphere& bsScene, Camera* cam); 75 | void CalculateShadowFrustum(int i, XMMATRIX VP); 76 | 77 | UINT m_Period; // the number of game milliseconds that each real time millisecond should count as. 78 | DirectionalLight m_dlSun; // light source representing the sun. 79 | DirectionalLight m_dlMoon; // light source representing the moon. 80 | time_point m_tLast; // the last point in time that we updated our cycle. 81 | float m_angleSun = 0.0f; 82 | bool m_isPaused = false; 83 | UINT m_sizeShadowMap; 84 | XMFLOAT4X4 m_amShadowViewProjs[4]; 85 | XMFLOAT4X4 m_amShadowViewProjTexs[4]; 86 | XMFLOAT4 m_aShadowFrustums[4][4]; 87 | }; 88 | 89 | -------------------------------------------------------------------------------- /Render Terrain/DirectionalLight.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Light.h 3 | 4 | Author: Chris Serson 5 | Last Edited: August 1, 2016 6 | 7 | Description: Class representing directional lights. 8 | */ 9 | #include "DirectionalLight.h" 10 | 11 | -------------------------------------------------------------------------------- /Render Terrain/DirectionalLight.h: -------------------------------------------------------------------------------- 1 | /* 2 | Light.h 3 | 4 | Author: Chris Serson 5 | Last Edited: August 1, 2016 6 | 7 | Description: Class representing directional lights. 8 | 9 | Usage: - Calling the constructor, either through DirectionalLight L(...); 10 | or DirectionalLight* L; L = new DirectionalLight(...);, will initialize. 11 | - Can also create a Light* L; L = new DirectionalLight(...); to instantiate 12 | with polymorphism. 13 | */ 14 | #pragma once 15 | #include "Light.h" 16 | 17 | class DirectionalLight : public Light { 18 | public: 19 | DirectionalLight() : Light() {} 20 | DirectionalLight(XMFLOAT4 amb, XMFLOAT4 dif, XMFLOAT4 spec, XMFLOAT3 dir) : Light(XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f), amb, dif, spec, XMFLOAT3(0.0f, 0.0f, 0.0f), 0.0f, dir, 0.0f) {} 21 | ~DirectionalLight() {} 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /Render Terrain/Frame.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Frame.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for handling all per frame interation for Render Terrain application. 8 | */ 9 | #include "Frame.h" 10 | #include 11 | 12 | Frame::Frame(unsigned int indexFrame, Device* dev, ResourceManager* rm, unsigned int h, unsigned int w, 13 | unsigned int dimShadowAtlas) : m_pDev(dev), m_pResMgr(rm), m_iFrame(indexFrame), m_hScreen(h), 14 | m_wScreen(w), m_wShadowAtlas(dimShadowAtlas), m_hShadowAtlas(dimShadowAtlas) { 15 | m_pCmdAllocator = nullptr; 16 | m_pBackBuffer = nullptr; 17 | m_pDepthStencilBuffer = nullptr; 18 | m_pFence = nullptr; 19 | m_hdlFenceEvent = nullptr; 20 | m_pFrameConstants = nullptr; 21 | m_pFrameConstantsMapped = nullptr; 22 | for (int i = 0; i < 4; ++i) { 23 | m_pShadowConstants[i] = nullptr; 24 | m_pShadowConstantsMapped[i] = nullptr; 25 | } 26 | 27 | m_pDev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, m_pCmdAllocator); 28 | 29 | m_pDev->GetBackBuffer(m_iFrame, m_pBackBuffer); 30 | m_pBackBuffer->SetName((L"Back Buffer " + std::to_wstring(m_iFrame)).c_str()); 31 | m_pResMgr->AddExistingResource(m_pBackBuffer); 32 | m_pResMgr->AddRTV(m_pBackBuffer, NULL, m_hdlBackBuffer); 33 | 34 | // create optimized clear value and default heap for DSB. 35 | D3D12_CLEAR_VALUE dsOptimizedClearValue = {}; 36 | dsOptimizedClearValue.Format = DXGI_FORMAT_D32_FLOAT; 37 | dsOptimizedClearValue.DepthStencil.Depth = 1.0f; 38 | dsOptimizedClearValue.DepthStencil.Stencil = 0; 39 | 40 | m_pResMgr->NewBuffer(m_pDepthStencilBuffer, 41 | &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT, m_wScreen, m_hScreen, 1, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL), 42 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_DEPTH_WRITE, &dsOptimizedClearValue); 43 | m_pDepthStencilBuffer->SetName((L"Depth/Stencil Buffer " + std::to_wstring(m_iFrame)).c_str()); 44 | 45 | // create the depth/stencil view 46 | D3D12_DEPTH_STENCIL_VIEW_DESC descDSV = {}; 47 | descDSV.Format = dsOptimizedClearValue.Format; 48 | descDSV.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; 49 | descDSV.Flags = D3D12_DSV_FLAG_NONE; 50 | m_pResMgr->AddDSV(m_pDepthStencilBuffer, &descDSV, m_hdlDSV); 51 | 52 | m_valFence = 0; 53 | m_pDev->CreateFence(m_valFence, D3D12_FENCE_FLAG_NONE, m_pFence); 54 | m_hdlFenceEvent = CreateEvent(NULL, false, false, NULL); 55 | if (!m_hdlFenceEvent) { 56 | throw GFX_Exception("Frame::Frame: Create Fence Event failed on init."); 57 | } 58 | 59 | InitShadowAtlas(); 60 | InitConstantBuffers(); 61 | } 62 | 63 | Frame::~Frame() { 64 | CloseHandle(m_hdlFenceEvent); 65 | 66 | if (m_pFence) { 67 | m_pFence->Release(); 68 | m_pFence = nullptr; 69 | } 70 | 71 | if (m_pCmdAllocator) { 72 | m_pCmdAllocator->Release(); 73 | m_pCmdAllocator = nullptr; 74 | } 75 | 76 | m_pDev = nullptr; 77 | m_pResMgr = nullptr; 78 | m_pDepthStencilBuffer = nullptr; 79 | m_pBackBuffer = nullptr; 80 | 81 | if (m_pFrameConstants) { 82 | m_pFrameConstants->Unmap(0, nullptr); 83 | m_pFrameConstantsMapped = nullptr; 84 | m_pFrameConstants = nullptr; 85 | } 86 | 87 | for (int i = 0; i < 4; ++i) { 88 | if (m_pShadowConstants[i]) { 89 | m_pShadowConstants[i]->Unmap(0, nullptr); 90 | m_pShadowConstantsMapped[i] = nullptr; 91 | m_pShadowConstants[i] = nullptr; 92 | } 93 | } 94 | } 95 | 96 | void Frame::InitShadowAtlas() { 97 | // shadow atlas is broken into 4 equal maps. Create a viewport and scissor rectangle for each. 98 | // to do so, divide atlas in 2 along both x and y axes. 99 | int w = m_wShadowAtlas / 2; 100 | int h = m_hShadowAtlas / 2; 101 | for (int i = 0; i < 2; ++i) { 102 | for (int j = 0; j < 2; ++j) { 103 | int index = i * 2 + j; 104 | m_vpShadowAtlas[index].TopLeftX = (float)(i * w); 105 | m_vpShadowAtlas[index].TopLeftY = (float)(j * h); 106 | m_vpShadowAtlas[index].Width = (float)w; 107 | m_vpShadowAtlas[index].Height = (float)h; 108 | m_vpShadowAtlas[index].MinDepth = 0; 109 | m_vpShadowAtlas[index].MaxDepth = 1; 110 | 111 | m_srShadowAtlas[index].left = i * w; 112 | m_srShadowAtlas[index].top = j * h; 113 | m_srShadowAtlas[index].right = m_srShadowAtlas[index].left + w; 114 | m_srShadowAtlas[index].bottom = m_srShadowAtlas[index].top + h; 115 | } 116 | } 117 | 118 | // Create the shadow map texture buffer. 119 | D3D12_RESOURCE_DESC descTex = {}; 120 | descTex.Alignment = 0; 121 | descTex.MipLevels = 1; 122 | descTex.Format = DXGI_FORMAT_R24G8_TYPELESS; 123 | descTex.Width = m_wShadowAtlas; 124 | descTex.Height = m_hShadowAtlas; 125 | descTex.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; 126 | descTex.DepthOrArraySize = 1; 127 | descTex.SampleDesc.Count = 1; 128 | descTex.SampleDesc.Quality = 0; 129 | descTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 130 | descTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; 131 | 132 | D3D12_CLEAR_VALUE clearValue; // Performance tip: Tell the runtime at resource creation the desired clear value. (per microsoft examples) 133 | clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; 134 | clearValue.DepthStencil.Depth = 1.0f; 135 | clearValue.DepthStencil.Stencil = 0; 136 | 137 | D3D12_DEPTH_STENCIL_VIEW_DESC descDSV = {}; 138 | descDSV.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; 139 | descDSV.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; 140 | descDSV.Texture2D.MipSlice = 0; 141 | descDSV.Flags = D3D12_DSV_FLAG_NONE; 142 | 143 | // Create the SRV for the heightmap texture and save to Terrain object. 144 | D3D12_SHADER_RESOURCE_VIEW_DESC descSRV = {}; 145 | descSRV.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 146 | descSRV.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; 147 | descSRV.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 148 | descSRV.Texture2D.MipLevels = descTex.MipLevels; 149 | descSRV.Texture2D.MostDetailedMip = 0; 150 | descSRV.Texture2D.ResourceMinLODClamp = 0.0f; 151 | descSRV.Texture2D.PlaneSlice = 0; 152 | 153 | m_pResMgr->NewBuffer(m_pShadowAtlas, &descTex, &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, &clearValue); 154 | m_pShadowAtlas->SetName((L"Shadow Atlas Texture " + std::to_wstring(m_iFrame)).c_str()); 155 | m_pResMgr->AddDSV(m_pShadowAtlas, &descDSV, m_hdlShadowAtlasDSV); 156 | m_pResMgr->AddSRV(m_pShadowAtlas, &descSRV, m_hdlShadowAtlasSRV_CPU, m_hdlShadowAtlasSRV_GPU); 157 | } 158 | 159 | void Frame::InitConstantBuffers() { 160 | // initialize frame constant buffer 161 | // Create an upload buffer for the CBV 162 | UINT64 sizeofBuffer = sizeof(PerFrameConstantBuffer); 163 | m_pResMgr->NewBuffer(m_pFrameConstants, &CD3DX12_RESOURCE_DESC::Buffer(sizeofBuffer), 164 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr); 165 | m_pFrameConstants->SetName((L"Frame Constant Buffer " + std::to_wstring(m_iFrame)).c_str()); 166 | 167 | // Create the CBV itself 168 | D3D12_CONSTANT_BUFFER_VIEW_DESC descCBV = {}; 169 | descCBV.BufferLocation = m_pFrameConstants->GetGPUVirtualAddress(); 170 | descCBV.SizeInBytes = (sizeofBuffer + 255) & ~255; // CB size is required to be 256-byte aligned. 171 | m_pResMgr->AddCBV(&descCBV, m_hdlFrameConstantsCBV_CPU, m_hdlFrameConstantsCBV_GPU); 172 | 173 | // initialize and map the constant buffers. 174 | // per the DirectX 12 sample code, we can leave this mapped until we close. 175 | CD3DX12_RANGE rangeRead(0, 0); // we won't be reading from this resource 176 | if (FAILED(m_pFrameConstants->Map(0, &rangeRead, reinterpret_cast(&m_pFrameConstantsMapped)))) { 177 | throw (GFX_Exception("Frame::InitConstantBuffers failed on Frame Constant Buffer.")); 178 | } 179 | 180 | // initialize constant buffer for each shadow map in atlas. 181 | for (int i = 0; i < 4; ++i) { 182 | // Create an upload buffer for the CBV 183 | sizeofBuffer = sizeof(ShadowMapShaderConstants); 184 | m_pResMgr->NewBuffer(m_pShadowConstants[i], &CD3DX12_RESOURCE_DESC::Buffer(sizeofBuffer), 185 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr); 186 | m_pShadowConstants[i]->SetName((L"Shadow Constant Buffer " + std::to_wstring(m_iFrame) + L", " + std::to_wstring(i)).c_str()); 187 | 188 | // Create the CBV itself 189 | descCBV.BufferLocation = m_pShadowConstants[i]->GetGPUVirtualAddress(); 190 | descCBV.SizeInBytes = (sizeofBuffer + 255) & ~255; // CB size is required to be 256-byte aligned. 191 | m_pResMgr->AddCBV(&descCBV, m_hdlShadowConstantsCBV_CPU[i], m_hdlShadowConstantsCBV_GPU[i]); 192 | 193 | // initialize and map the constant buffers. 194 | if (FAILED(m_pShadowConstants[i]->Map(0, &rangeRead, reinterpret_cast(&m_pShadowConstantsMapped[i])))) { 195 | throw (GFX_Exception(("Frame::InitConstantBuffers failed on Shadow Constant Buffer " + std::to_string(i)).c_str())); 196 | } 197 | } 198 | } 199 | 200 | // Wait for GPU to signal it has completed with the previous iteration of this frame. 201 | void Frame::WaitForGPU() { 202 | // add fence signal. 203 | m_pDev->SetFence(m_pFence, m_valFence); 204 | 205 | // if the current value returned by the fence is less than the current fence value for this buffer, then we know the GPU is not done with the buffer, so wait. 206 | if (FAILED(m_pFence->SetEventOnCompletion(m_valFence, m_hdlFenceEvent))) { 207 | throw GFX_Exception("Frame::WaitForGPU failed to SetEventOnCompletion."); 208 | } 209 | 210 | WaitForSingleObject(m_hdlFenceEvent, INFINITE); 211 | 212 | ++m_valFence; 213 | } 214 | 215 | // Confirm the previous GPU activity on this frame is completed and reset the command allocator for the next run. 216 | void Frame::Reset() { 217 | WaitForGPU(); 218 | 219 | // reset command allocator. 220 | // This appears to only be necessary when you want to associate a different command list with the command allocator. 221 | // You can simply reset the same command list and keep going without resetting the command allocator. 222 | // if (FAILED(m_pCmdAllocator->Reset())) { 223 | // throw GFX_Exception("Frame::Reset: m_pCmdAllocator Reset failed."); 224 | // } 225 | } 226 | 227 | // Resets a command list for use with this frame. 228 | void Frame::AttachCommandList(ID3D12GraphicsCommandList* cmdList) { 229 | if (FAILED(cmdList->Reset(m_pCmdAllocator, NULL))) { 230 | throw GFX_Exception("Frame::AttachCommandList: CommandList Reset failed."); 231 | } 232 | } 233 | 234 | // Set the Frame Constant buffer. 235 | void Frame::SetFrameConstants(PerFrameConstantBuffer frameConstants) { 236 | memcpy(m_pFrameConstantsMapped, &frameConstants, sizeof(PerFrameConstantBuffer)); 237 | } 238 | 239 | // Set the Shadow Constant buffer. i refers to which shadow map you're setting the constants for. 240 | void Frame::SetShadowConstants(ShadowMapShaderConstants shadowConstants, unsigned int i) { 241 | memcpy(m_pShadowConstantsMapped[i], &shadowConstants, sizeof(ShadowMapShaderConstants)); 242 | } 243 | 244 | // Call at the start of rendering the shadow passes to switch the shadow atlas to write mode. 245 | void Frame::BeginShadowPass(ID3D12GraphicsCommandList* cmdList) { 246 | cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pShadowAtlas, 247 | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE)); 248 | 249 | cmdList->ClearDepthStencilView(m_hdlShadowAtlasDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); 250 | cmdList->OMSetRenderTargets(0, nullptr, false, &m_hdlShadowAtlasDSV); 251 | } 252 | 253 | // Call at the end of rendering the shadow passes to switch the shadow atlas to read mode. 254 | void Frame::EndShadowPass(ID3D12GraphicsCommandList* cmdList) { 255 | cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pShadowAtlas, 256 | D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); 257 | } 258 | 259 | // Sets the back buffer for rendering and makes it the render target. Clears to clearColor. 260 | void Frame::BeginRenderPass(ID3D12GraphicsCommandList* cmdList, const float clearColor[4]) { 261 | // set back buffer to render target. 262 | cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pBackBuffer, 263 | D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); 264 | 265 | cmdList->ClearRenderTargetView(m_hdlBackBuffer, clearColor, 0, NULL); 266 | cmdList->ClearDepthStencilView(m_hdlDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); 267 | 268 | // get the handle to the back buffer and set as render target. 269 | cmdList->OMSetRenderTargets(1, &m_hdlBackBuffer, false, &m_hdlDSV); 270 | } 271 | 272 | // Sets the back buffer to present. 273 | void Frame::EndRenderPass(ID3D12GraphicsCommandList* cmdList) { 274 | cmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pBackBuffer, 275 | D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); 276 | } 277 | 278 | // Attach the shadow pass resources to the provided command list for shadow pass i. 279 | // Requires the index into the root descriptor table to attach the shadow constant buffer to. 280 | void Frame::AttachShadowPassResources(unsigned int i, ID3D12GraphicsCommandList* cmdList, 281 | unsigned int cbvDescTableIndex) { 282 | cmdList->RSSetViewports(1, &m_vpShadowAtlas[i]); 283 | cmdList->RSSetScissorRects(1, &m_srShadowAtlas[i]); 284 | 285 | cmdList->SetGraphicsRootDescriptorTable(cbvDescTableIndex, m_hdlShadowConstantsCBV_GPU[i]); 286 | } 287 | 288 | // Attach the frame resources needed for the normal render pass. 289 | // Requires the indices into the root descriptor table to attach the shadow srv and shadow constant buffer to. 290 | void Frame::AttachFrameResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex, 291 | unsigned int cbvDescTableIndex) { 292 | cmdList->SetGraphicsRootDescriptorTable(srvDescTableIndex, m_hdlShadowAtlasSRV_GPU); 293 | cmdList->SetGraphicsRootDescriptorTable(cbvDescTableIndex, m_hdlFrameConstantsCBV_GPU); 294 | } -------------------------------------------------------------------------------- /Render Terrain/Frame.h: -------------------------------------------------------------------------------- 1 | /* 2 | Frame.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for handling all per frame interation for Render Terrain application. 8 | 9 | Usage: - Frame F(...); 10 | - Frame* F; F = new Frame(...); 11 | - Proper shutdown is handled by the destructor. 12 | - Create a Frame object for each frame, ie 3 for triple buffering. 13 | 14 | Future Work: - Add support for multi-threading. 15 | */ 16 | #pragma once 17 | 18 | #include "ResourceManager.h" 19 | #include "Light.h" 20 | 21 | using namespace graphics; 22 | 23 | struct PerFrameConstantBuffer { 24 | XMFLOAT4X4 viewproj; 25 | XMFLOAT4X4 shadowtexmatrices[4]; 26 | XMFLOAT4 eye; 27 | XMFLOAT4 frustum[6]; 28 | LightSource light; 29 | BOOL useTextures; 30 | }; 31 | 32 | struct ShadowMapShaderConstants { 33 | XMFLOAT4X4 shadowViewProj; 34 | XMFLOAT4 eye; 35 | XMFLOAT4 frustum[4]; 36 | }; 37 | 38 | class Frame { 39 | public: 40 | Frame(unsigned int indexFrame, Device* dev, ResourceManager* rm, unsigned int h, unsigned int w, 41 | unsigned int dimShadowAtlas = 4096); 42 | ~Frame(); 43 | 44 | ID3D12CommandAllocator* GetAllocator() { return m_pCmdAllocator; } 45 | 46 | // Confirm the previous GPU activity on this frame is completed and reset the command allocator for the next run. 47 | void Reset(); 48 | // Resets a command list for use with this frame. 49 | void AttachCommandList(ID3D12GraphicsCommandList* cmdList); 50 | 51 | // Set the Frame Constant buffer. 52 | void SetFrameConstants(PerFrameConstantBuffer frameConstants); 53 | // Set the Shadow Constant buffer. i refers to which shadow map you're setting the constants for. 54 | void SetShadowConstants(ShadowMapShaderConstants shadowConstants, unsigned int i); 55 | 56 | // Call at the start of rendering the shadow passes to switch the shadow atlas to write mode. 57 | void BeginShadowPass(ID3D12GraphicsCommandList* cmdList); 58 | // Call at the end of rendering the shadow passes to switch the shadow atlas to read mode. 59 | void EndShadowPass(ID3D12GraphicsCommandList* cmdList); 60 | // Sets the back buffer for rendering and makes it the render target. Clears to clearColor. 61 | void BeginRenderPass(ID3D12GraphicsCommandList* cmdList, const float clearColor[4]); 62 | // Sets the back buffer to present. 63 | void EndRenderPass(ID3D12GraphicsCommandList* cmdList); 64 | // Attach the shadow pass resources to the provided command list for shadow pass i. 65 | // Requires the index into the root descriptor table to attach the shadow constant buffer to. 66 | void AttachShadowPassResources(unsigned int i, ID3D12GraphicsCommandList* cmdList, 67 | unsigned int cbvDescTableIndex); 68 | // Attach the frame resources needed for the normal render pass. 69 | // Requires the indices into the root descriptor table to attach the shadow srv and shadow constant buffer to. 70 | void AttachFrameResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex, 71 | unsigned int cbvDescTableIndex); 72 | 73 | private: 74 | void InitShadowAtlas(); 75 | void InitConstantBuffers(); 76 | 77 | // Wait for GPU to signal it has completed with the previous iteration of this frame. 78 | void WaitForGPU(); 79 | 80 | Device* m_pDev; 81 | ResourceManager* m_pResMgr; 82 | ID3D12CommandAllocator* m_pCmdAllocator; 83 | ID3D12Resource* m_pBackBuffer; 84 | ID3D12Resource* m_pDepthStencilBuffer; 85 | ID3D12Resource* m_pShadowAtlas; 86 | ID3D12Resource* m_pFrameConstants; 87 | ID3D12Resource* m_pShadowConstants[4]; 88 | ID3D12Fence* m_pFence; 89 | HANDLE m_hdlFenceEvent; 90 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlBackBuffer; 91 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlDSV; 92 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlShadowAtlasDSV; 93 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlShadowAtlasSRV_CPU; 94 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlShadowAtlasSRV_GPU; 95 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlFrameConstantsCBV_CPU; 96 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlFrameConstantsCBV_GPU; 97 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlShadowConstantsCBV_CPU[4]; 98 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlShadowConstantsCBV_GPU[4]; 99 | D3D12_VIEWPORT m_vpShadowAtlas[4]; 100 | D3D12_RECT m_srShadowAtlas[4]; 101 | PerFrameConstantBuffer* m_pFrameConstantsMapped; 102 | ShadowMapShaderConstants* m_pShadowConstantsMapped[4]; 103 | unsigned long long m_valFence; // Value to check fence against to confirm GPU is done. 104 | unsigned int m_iFrame; // Which frame number is this frame? 105 | unsigned int m_wScreen; 106 | unsigned int m_hScreen; 107 | unsigned int m_wShadowAtlas; 108 | unsigned int m_hShadowAtlas; 109 | }; 110 | 111 | -------------------------------------------------------------------------------- /Render Terrain/Graphics.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Graphics.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating and managing a Direct3D 12 instance. 8 | */ 9 | #include "Graphics.h" 10 | #include 11 | 12 | namespace graphics { 13 | /* Definitions for Non-class-specific Functions. */ 14 | // Compile the specified shader. 15 | void CompileShader(LPCWSTR fn, ShaderType st, D3D12_SHADER_BYTECODE& bcShader) { 16 | LPCSTR version; 17 | switch (st) { 18 | case PIXEL_SHADER: 19 | version = "ps_5_0"; 20 | break; 21 | case VERTEX_SHADER: 22 | version = "vs_5_0"; 23 | break; 24 | case GEOMETRY_SHADER: 25 | version = "gs_5_0"; 26 | break; 27 | case HULL_SHADER: 28 | version = "hs_5_0"; 29 | break; 30 | case DOMAIN_SHADER: 31 | version = "ds_5_0"; 32 | break; 33 | default: 34 | version = ""; // will break on attempting to compile as not valid. 35 | } 36 | 37 | ID3DBlob* shader; 38 | ID3DBlob* err; 39 | if (FAILED(D3DCompileFromFile(fn, NULL, NULL, "main", version, D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &shader, &err))) { 40 | if (shader) shader->Release(); 41 | if (err) { 42 | std::string msg((char *)err->GetBufferPointer()); 43 | msg = "graphics::CompileShader: " + msg; 44 | throw GFX_Exception(msg.c_str()); 45 | } 46 | else { 47 | std::string msg(version); 48 | msg = "graphics::CompileShader: Failed to compile shader version " + msg + ". No error returned from compiler"; 49 | throw GFX_Exception(msg.c_str()); 50 | } 51 | } 52 | bcShader.BytecodeLength = shader->GetBufferSize(); 53 | bcShader.pShaderBytecode = shader->GetBufferPointer(); 54 | } 55 | 56 | /* Definitions for Device Class */ 57 | Device::Device(HWND win, unsigned int h, unsigned int w, bool fullscreen, unsigned int numFrames) : 58 | m_hScreen(h), m_wScreen(w), m_isWindowed(!fullscreen), m_numFrames(numFrames) { 59 | m_pDev = nullptr; 60 | m_pCmdQ = nullptr; 61 | m_pSwapChain = nullptr; 62 | 63 | // Create a DirectX graphics interface factory. 64 | IDXGIFactory4* factory; 65 | if (FAILED(CreateDXGIFactory2(FACTORY_DEBUG, IID_PPV_ARGS(&factory)))) 66 | { 67 | throw GFX_Exception("In Device::Device: CreateDXGIFactory2 failed."); 68 | } 69 | 70 | // Search for a DirectX 12 compatible Hardware device (ie graphics card). Minimum feature level = 11.0 71 | int adapterIndex = 0; 72 | bool adapterFound = false; 73 | IDXGIAdapter1* adapter; 74 | while (factory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND) { 75 | DXGI_ADAPTER_DESC1 desc; 76 | adapter->GetDesc1(&desc); 77 | 78 | if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { 79 | // we don't want a software device (emulator). 80 | ++adapterIndex; 81 | continue; 82 | } 83 | 84 | if (SUCCEEDED(D3D12CreateDevice(adapter, FEATURE_LEVEL, _uuidof(ID3D12Device), NULL))) { 85 | adapterFound = true; 86 | break; 87 | } 88 | 89 | ++adapterIndex; 90 | } 91 | if (!adapterFound) { 92 | throw GFX_Exception("In Device::Device: No DirectX 12 compatible graphics card found on init."); 93 | } 94 | 95 | // attempt to create the device. 96 | if (FAILED(D3D12CreateDevice(adapter, FEATURE_LEVEL, IID_PPV_ARGS(&m_pDev)))) { 97 | throw GFX_Exception("In Device::Device: D3D12CreateDevice failed on init."); 98 | } 99 | 100 | // attempt to create the command queue. 101 | D3D12_COMMAND_QUEUE_DESC descCmdQ = {}; 102 | descCmdQ.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; 103 | descCmdQ.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; 104 | descCmdQ.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 105 | descCmdQ.NodeMask = 0; 106 | 107 | if (FAILED(m_pDev->CreateCommandQueue(&descCmdQ, IID_PPV_ARGS(&m_pCmdQ)))) { 108 | throw GFX_Exception("In Device::Device: CreateCommandQueue failed on init."); 109 | } 110 | 111 | // attempt to create the swap chain. 112 | DXGI_SAMPLE_DESC descSample = {}; 113 | descSample.Count = 1; // turns multi-sampling off. Not supported feature for my card. 114 | DXGI_SWAP_CHAIN_DESC descSwapChain = {}; 115 | descSwapChain.BufferCount = m_numFrames; 116 | descSwapChain.BufferDesc.Height = m_hScreen; 117 | descSwapChain.BufferDesc.Width = m_wScreen; 118 | descSwapChain.BufferDesc.Format = DESIRED_FORMAT; 119 | descSwapChain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // this says the pipeline will render to this swap chain 120 | descSwapChain.OutputWindow = win; 121 | descSwapChain.Windowed = m_isWindowed; 122 | descSwapChain.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 123 | descSwapChain.SampleDesc = descSample; 124 | 125 | // create temporary swapchain. 126 | IDXGISwapChain* swapChain; 127 | if (FAILED(factory->CreateSwapChain(m_pCmdQ, &descSwapChain, &swapChain))) { 128 | throw GFX_Exception("In Device::Device: CreateSwapChain failed on init."); 129 | } 130 | // upgrade swapchain to swapchain3 and store in mpSwapChain. 131 | m_pSwapChain = static_cast(swapChain); 132 | 133 | // clean up. 134 | swapChain = NULL; 135 | if (factory) { 136 | factory->Release(); 137 | factory = NULL; 138 | } 139 | } 140 | 141 | Device::~Device() { 142 | if (m_pSwapChain) { 143 | m_pSwapChain->SetFullscreenState(false, NULL); // ensure swap chain in windowed mode before releasing. 144 | m_pSwapChain->Release(); 145 | m_pSwapChain = nullptr; 146 | } 147 | 148 | if (m_pCmdQ) { 149 | m_pCmdQ->Release(); 150 | m_pCmdQ = nullptr; 151 | } 152 | if (m_pDev) { 153 | m_pDev->Release(); 154 | m_pDev = nullptr; 155 | } 156 | } 157 | 158 | // Return the heap descriptor size for the specified heap type. 159 | unsigned int Device::GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE ht) { 160 | return m_pDev->GetDescriptorHandleIncrementSize(ht); 161 | } 162 | 163 | // Create and return a pointer to a Command Allocator 164 | void Device::CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE clt, ID3D12CommandAllocator*& allocator) { 165 | // attempt to create a command allocator. 166 | if (FAILED(m_pDev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)))) { 167 | throw GFX_Exception("Device::CreateCommandAllocator failed."); 168 | } 169 | } 170 | 171 | // Return a pointer to the specified back buffer 172 | void Device::GetBackBuffer(unsigned int i, ID3D12Resource*& buffer) { 173 | if (i >= m_numFrames || i < 0) { 174 | throw GFX_Exception("Invalid buffer index provided to Device::GetBackBuffer."); 175 | } 176 | 177 | if (FAILED(m_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&buffer)))) { 178 | throw GFX_Exception("Swap Chain GetBuffer failed in Device::GetBackBuffer."); 179 | } 180 | } 181 | 182 | // Return the index of the initial back buffer 183 | unsigned int Device::GetCurrentBackBuffer() { 184 | return m_pSwapChain->GetCurrentBackBufferIndex(); 185 | } 186 | 187 | // Create and return a pointer to a new root signature matching the provided description. 188 | void Device::CreateRootSig(CD3DX12_ROOT_SIGNATURE_DESC* desc, ID3D12RootSignature*& root) { 189 | ID3DBlob* err; 190 | ID3DBlob* sig; 191 | 192 | if (FAILED(D3D12SerializeRootSignature(desc, D3D_ROOT_SIGNATURE_VERSION_1, &sig, &err))) { 193 | std::string msg((char *)err->GetBufferPointer()); 194 | msg = "In Device::CreateRootSig: " + msg; 195 | throw GFX_Exception(msg.c_str()); 196 | } 197 | if (FAILED(m_pDev->CreateRootSignature(0, sig->GetBufferPointer(), sig->GetBufferSize(), IID_PPV_ARGS(&root)))) { 198 | throw GFX_Exception("Device::CreateRootSig failed."); 199 | } 200 | sig->Release(); 201 | } 202 | 203 | // Create and return a pointer to a new Pipeline State Object matching the provided description. 204 | void Device::CreatePSO(D3D12_GRAPHICS_PIPELINE_STATE_DESC* desc, ID3D12PipelineState*& pso) { 205 | if (FAILED(m_pDev->CreateGraphicsPipelineState(desc, IID_PPV_ARGS(&pso)))) { 206 | throw GFX_Exception("Device::CreateGraphicsPipeline failed."); 207 | } 208 | } 209 | 210 | // Create and return a pointer to a Descriptor Heap. 211 | void Device::CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_DESC* desc, ID3D12DescriptorHeap*& heap) { 212 | if FAILED(m_pDev->CreateDescriptorHeap(desc, IID_PPV_ARGS(&heap))) { 213 | throw GFX_Exception("Device::CreateDescriptorHeap failed."); 214 | } 215 | } 216 | 217 | // Create a Shader Resource view for the supplied resource. 218 | void Device::CreateSRV(ID3D12Resource*& tex, D3D12_SHADER_RESOURCE_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle) { 219 | m_pDev->CreateShaderResourceView(tex, desc, handle); 220 | } 221 | 222 | // Create a constant buffer view 223 | void Device::CreateCBV(D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle) { 224 | m_pDev->CreateConstantBufferView(desc, handle); 225 | } 226 | 227 | // Create a depth/stencil buffer view 228 | void Device::CreateDSV(ID3D12Resource*& tex, D3D12_DEPTH_STENCIL_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle) { 229 | m_pDev->CreateDepthStencilView(tex, desc, handle); 230 | } 231 | 232 | // Create a render target view 233 | void Device::CreateRTV(ID3D12Resource*& tex, D3D12_RENDER_TARGET_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle) { 234 | m_pDev->CreateRenderTargetView(tex, desc, handle); 235 | } 236 | 237 | // Create a sampler 238 | void Device::CreateSampler(D3D12_SAMPLER_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle) { 239 | m_pDev->CreateSampler(desc, handle); 240 | } 241 | 242 | // Create a fence 243 | void Device::CreateFence(unsigned long long valInit, D3D12_FENCE_FLAGS flags, ID3D12Fence*& fence) { 244 | if (FAILED(m_pDev->CreateFence(valInit, flags, IID_PPV_ARGS(&fence)))) { 245 | throw GFX_Exception("Device::CreateFence failed on init."); 246 | } 247 | } 248 | 249 | // Create a Command List 250 | void Device::CreateGraphicsCommandList(D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator* alloc, ID3D12GraphicsCommandList*& list, unsigned int mask, 251 | ID3D12PipelineState* psoInit) { 252 | // create a command list. 253 | if (FAILED(m_pDev->CreateCommandList(mask, type, alloc, psoInit, IID_PPV_ARGS(&list)))) { 254 | throw GFX_Exception("Device::CreateCommandList failed."); 255 | } 256 | } 257 | 258 | // Create a commited resource. 259 | void Device::CreateCommittedResource(ID3D12Resource*& heap, D3D12_RESOURCE_DESC* desc, 260 | D3D12_HEAP_PROPERTIES* props, D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear) { 261 | /*if (FAILED(m_pDev->CreateCommittedResource(props, flags, desc, state, clear, IID_PPV_ARGS(&heap)))) { 262 | throw GFX_Exception("Device::CreateCommittedResource failed."); 263 | }*/ 264 | HRESULT hr = m_pDev->CreateCommittedResource(props, flags, desc, state, clear, IID_PPV_ARGS(&heap)); 265 | if (FAILED(hr)) { 266 | hr = m_pDev->GetDeviceRemovedReason(); 267 | throw GFX_Exception("Device::CreateCommittedResource failed."); 268 | } 269 | } 270 | 271 | // Signal Command Queue with provided fence value. 272 | void Device::SetFence(ID3D12Fence* fence, unsigned long long val) { 273 | // Add Signal command to set fence to the fence value that indicates the GPU is done with that buffer. 274 | if (FAILED(m_pCmdQ->Signal(fence, val))) { 275 | throw GFX_Exception("Device::SetFence failed."); 276 | } 277 | } 278 | 279 | // Run the submitted array of commands 280 | void Device::ExecuteCommandLists(ID3D12CommandList* lCmds[], unsigned int numCommands) { 281 | // execute 282 | m_pCmdQ->ExecuteCommandLists(numCommands, lCmds); 283 | } 284 | 285 | // Present the latest back buffer on the swap chain. 286 | void Device::Present() { 287 | // swap the back buffers. 288 | if (FAILED(m_pSwapChain->Present(0, 0))) { 289 | throw GFX_Exception("Device::Present SwapChain failed to present."); 290 | } 291 | } 292 | } -------------------------------------------------------------------------------- /Render Terrain/Graphics.h: -------------------------------------------------------------------------------- 1 | /* 2 | Graphics.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating and managing a Direct3D 12 instance. 8 | 9 | Usage: - Calling the constructor, either through Device DEV(...); 10 | or Device* DEV; DEV = new Device(...);, will find a 11 | Direct3D 12 compatible hardware device and initialize 12 | Direct3D on it. 13 | - Proper shutdown is handled by the destructor. 14 | - All requests to the graphics device must go through the 15 | Device object. 16 | - The calling object can request a Command List from the Graphics 17 | object (CreateGraphicsCommandList()). 18 | - The calling object must tell the Graphics object when to 19 | reset the pipeline for a new frame (ResetPipeline()), 20 | when to swap the buffers (SetBackBufferRender(), SetBackBufferPresent(), 21 | and when to actually execute the command list (Render()). 22 | 23 | Future Work: - Add support for compute shaders. 24 | - Add support for bundles. 25 | - Add support for reserved and placed resources. 26 | */ 27 | #pragma once 28 | 29 | // Linkages 30 | #pragma comment(lib, "d3d12.lib") 31 | #pragma comment(lib, "dxgi.lib") 32 | #pragma comment(lib, "d3dcompiler.lib") 33 | 34 | #include "D3DX12.h" 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | namespace graphics { 41 | using namespace DirectX; 42 | 43 | static const float SCREEN_DEPTH = 1000.0f; 44 | static const float SCREEN_NEAR = 0.1f; 45 | static const UINT FACTORY_DEBUG = DXGI_CREATE_FACTORY_DEBUG; // set to 0 if not debugging, DXGI_CREATE_FACTORY_DEBUG if debugging. 46 | static const DXGI_FORMAT DESIRED_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; 47 | static const D3D_FEATURE_LEVEL FEATURE_LEVEL = D3D_FEATURE_LEVEL_11_0; // minimum feature level necessary for DirectX 12 compatibility. 48 | // this is all my current card supports. 49 | enum ShaderType { PIXEL_SHADER, VERTEX_SHADER, GEOMETRY_SHADER, HULL_SHADER, DOMAIN_SHADER }; 50 | 51 | class GFX_Exception : public std::runtime_error { 52 | public: 53 | GFX_Exception(const char *msg) : std::runtime_error(msg) {} 54 | }; 55 | 56 | // Compile the specified shader. 57 | void CompileShader(LPCWSTR fn, ShaderType st, D3D12_SHADER_BYTECODE& bcShader); 58 | 59 | class Device { 60 | public: 61 | Device(HWND win, unsigned int h, unsigned int w, bool fullscreen = false, unsigned int numFrames = 3); 62 | ~Device(); 63 | 64 | // Return the heap descriptor size for the specified heap type. 65 | unsigned int GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE ht); 66 | 67 | // Create and return a pointer to a Command Allocator 68 | void CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE clt, ID3D12CommandAllocator*& allocator); 69 | // Return a pointer to the specified back buffer 70 | void GetBackBuffer(unsigned int i, ID3D12Resource*& buffer); 71 | // Return the index of the initial back buffer 72 | unsigned int GetCurrentBackBuffer(); 73 | // Signal Command Queue with provided fence value. 74 | void SetFence(ID3D12Fence* fence, unsigned long long val); 75 | 76 | // Create and return a pointer to a new root signature matching the provided description. 77 | void CreateRootSig(CD3DX12_ROOT_SIGNATURE_DESC* desc, ID3D12RootSignature*& root); 78 | // Create and return a pointer to a new Pipeline State Object matching the provided description. 79 | void CreatePSO(D3D12_GRAPHICS_PIPELINE_STATE_DESC* desc, ID3D12PipelineState*& pso); 80 | 81 | // Create and return a pointer to a Descriptor Heap. 82 | void CreateDescriptorHeap(D3D12_DESCRIPTOR_HEAP_DESC* desc, ID3D12DescriptorHeap*& heap); 83 | // Create a Shader Resource view for the supplied resource. 84 | void CreateSRV(ID3D12Resource*& tex, D3D12_SHADER_RESOURCE_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle); 85 | // Create a constant buffer view 86 | void CreateCBV(D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle); 87 | // Create a depth/stencil buffer view 88 | void CreateDSV(ID3D12Resource*& tex, D3D12_DEPTH_STENCIL_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle); 89 | // Create a render target view 90 | void CreateRTV(ID3D12Resource*& tex, D3D12_RENDER_TARGET_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle); 91 | // Create a sampler 92 | void CreateSampler(D3D12_SAMPLER_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE handle); 93 | // Create a fence 94 | void CreateFence(unsigned long long valInit, D3D12_FENCE_FLAGS flags, ID3D12Fence*& fence); 95 | // Create a Command List 96 | void CreateGraphicsCommandList(D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator* alloc, ID3D12GraphicsCommandList*& list, 97 | unsigned int mask = 0, ID3D12PipelineState* psoInit = nullptr); 98 | 99 | // Create a commited resource. 100 | void CreateCommittedResource(ID3D12Resource*& heap, D3D12_RESOURCE_DESC* desc, D3D12_HEAP_PROPERTIES* props, D3D12_HEAP_FLAGS flags, 101 | D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear); 102 | 103 | // Run the submitted array of commands 104 | void ExecuteCommandLists(ID3D12CommandList* lCmds[], unsigned int numCommands); 105 | // Present the latest back buffer on the swap chain. 106 | void Present(); 107 | 108 | private: 109 | ID3D12Device* m_pDev; 110 | ID3D12CommandQueue* m_pCmdQ; 111 | IDXGISwapChain3* m_pSwapChain; 112 | unsigned int m_wScreen; 113 | unsigned int m_hScreen; 114 | unsigned int m_numFrames; 115 | bool m_isWindowed; 116 | }; 117 | }; 118 | -------------------------------------------------------------------------------- /Render Terrain/Light.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Light.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class representing lights. 8 | */ 9 | #include "Light.h" 10 | 11 | Light::Light(XMFLOAT4 p, XMFLOAT4 amb, XMFLOAT4 dif, XMFLOAT4 spec, XMFLOAT3 att, float r, XMFLOAT3 dir, float sexp) { 12 | m_lsLight.pos = p; 13 | m_lsLight.intensityAmbient = amb; 14 | m_lsLight.intensityDiffuse = dif; 15 | m_lsLight.intensitySpecular = spec; 16 | m_lsLight.attenuation = att; 17 | m_lsLight.range = r; 18 | m_lsLight.spotlightConeExponent = sexp; 19 | 20 | XMVECTOR v = XMLoadFloat3(&dir); 21 | v = XMVector3Normalize(v); 22 | XMStoreFloat3(&m_lsLight.direction, v); 23 | } -------------------------------------------------------------------------------- /Render Terrain/Light.h: -------------------------------------------------------------------------------- 1 | /* 2 | Light.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class representing lights. 8 | 9 | Usage: - While you can instantiate a Light, it is better to 10 | create a child class for each type of light. 11 | ie. directional, point, spot. 12 | */ 13 | #pragma once 14 | 15 | #include 16 | #include 17 | 18 | using namespace DirectX; 19 | 20 | struct LightSource { 21 | LightSource() { ZeroMemory(this, sizeof(this)); } 22 | 23 | XMFLOAT4 pos; 24 | XMFLOAT4 intensityAmbient; 25 | XMFLOAT4 intensityDiffuse; 26 | XMFLOAT4 intensitySpecular; 27 | XMFLOAT3 attenuation; 28 | float range; 29 | XMFLOAT3 direction; 30 | float spotlightConeExponent; 31 | }; 32 | 33 | class Light { 34 | public: 35 | Light() {} 36 | Light(XMFLOAT4 p, XMFLOAT4 amb, XMFLOAT4 dif, XMFLOAT4 spec, XMFLOAT3 att, float r, XMFLOAT3 dir, float sexp); 37 | ~Light() {} 38 | 39 | LightSource GetLight() { return m_lsLight; } 40 | void SetLightDirection(XMFLOAT3 dir) { m_lsLight.direction = dir; } 41 | void SetDiffuseColor(XMFLOAT4 c) { m_lsLight.intensityDiffuse = c; } 42 | void SetSpecularColor(XMFLOAT4 c) { m_lsLight.intensitySpecular = c; } 43 | 44 | protected: 45 | LightSource m_lsLight; 46 | }; 47 | 48 | -------------------------------------------------------------------------------- /Render Terrain/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Main.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Render Terrain - Win32/DirectX 12 application. 8 | Loads and displays a heightmap as a terrain. 9 | Press T to toggle between textured or coloured. 10 | Press 1 for 2D view. 11 | Press 2 for Shadow Maps. 12 | Press 3 for 3D view. 13 | */ 14 | #include "Window.h" 15 | #include "Scene.h" 16 | #include // included for mouse input stuff 17 | 18 | using namespace std; 19 | using namespace graphics; 20 | using namespace window; 21 | 22 | static const LPCWSTR appName = L"Directx 12 Terrain Renderer"; 23 | static const int WINDOW_HEIGHT = 1080; // dimensions for the window we're making. 24 | static const int WINDOW_WIDTH = 1920; 25 | //static const int WINDOW_HEIGHT = 2160; // dimensions for the window we're making. 26 | //static const int WINDOW_WIDTH = 3840; 27 | 28 | static const bool FULL_SCREEN = false; 29 | static Scene* pScene = nullptr; 30 | static int lastMouseX = -1; 31 | static int lastMouseY = -1; 32 | 33 | static void KeyUp(UINT key) { 34 | switch (key) { 35 | case VK_ESCAPE: 36 | PostQuitMessage(0); 37 | return; 38 | } 39 | } 40 | 41 | static void KeyDown(UINT key) { 42 | switch (key) { 43 | case VK_SPACE: 44 | case _W: 45 | case _S: 46 | case _A: 47 | case _D: 48 | case _Q: 49 | case _Z: 50 | case _1: 51 | case _2: 52 | case _T: 53 | case _L: 54 | pScene->HandleKeyboardInput(key); 55 | break; 56 | } 57 | } 58 | 59 | static void HandleMouseMove(LPARAM lp) { 60 | int x = GET_X_LPARAM(lp); 61 | int y = GET_Y_LPARAM(lp); 62 | 63 | if (lastMouseX == -1) { // then we haven't actually moved the mouse yet, we're just starting. 64 | // do nothing 65 | } else { // calculate how far we've moved from the last time we updated and pass that info to the scene. 66 | // clamp the values as well so that when we're in windowed mode, we don't cause massive jumps when the 67 | // mouse moves out of the window and then back in at a completely different position. 68 | int moveX = lastMouseX - x; 69 | moveX = moveX > 20 ? 20 : moveX; 70 | moveX = moveX < -20 ? -20 : moveX; 71 | int moveY = lastMouseY - y; 72 | moveY = moveY > 20 ? 20 : moveY; 73 | moveY = moveY < -20 ? -20 : moveY; 74 | pScene->HandleMouseInput(moveX, moveY); 75 | } 76 | 77 | // update our last mouse coordinates; 78 | lastMouseX = x; 79 | lastMouseY = y; 80 | } 81 | 82 | static LRESULT CALLBACK WndProc(HWND win , UINT msg, WPARAM wp, LPARAM lp) { 83 | switch (msg) { 84 | case WM_DESTROY: 85 | case WM_CLOSE: 86 | PostQuitMessage(0); 87 | return 0; 88 | case WM_KEYUP: 89 | KeyUp((UINT)wp); 90 | break; 91 | case WM_KEYDOWN: 92 | KeyDown((UINT)wp); 93 | break; 94 | case WM_MOUSEMOVE: 95 | HandleMouseMove(lp); 96 | break; 97 | default: 98 | return DefWindowProc(win, msg, wp, lp); 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR cmdLine, int cmdShow) { 105 | try { 106 | Window WIN(appName, WINDOW_HEIGHT, WINDOW_WIDTH, WndProc, FULL_SCREEN); 107 | Device DEV(WIN.GetWindow(), WIN.Height(), WIN.Width()); 108 | Scene S(WIN.Height(), WIN.Width(), &DEV); 109 | pScene = &S; // create a pointer to the scene for access outside of main. 110 | 111 | MSG msg; 112 | ZeroMemory(&msg, sizeof(MSG)); 113 | 114 | while (1) { 115 | if (PeekMessage(&msg, WIN.GetWindow(), 0, 0, PM_REMOVE)) { 116 | TranslateMessage(&msg); 117 | DispatchMessage(&msg); 118 | } 119 | if (msg.message == WM_QUIT) { 120 | pScene = nullptr; 121 | return 1; 122 | } 123 | 124 | S.Update(); 125 | } 126 | 127 | pScene = nullptr; 128 | return 2; 129 | } catch (GFX_Exception& e) { 130 | OutputDebugStringA(e.what()); 131 | pScene = nullptr; 132 | return 3; 133 | } catch (Window_Exception& e) { 134 | OutputDebugStringA(e.what()); 135 | pScene = nullptr; 136 | return 4; 137 | } 138 | } -------------------------------------------------------------------------------- /Render Terrain/Material.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Material.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Classes for creating and managing Direct3D 12 materials. 8 | */ 9 | #include "Material.h" 10 | 11 | TerrainMaterial::TerrainMaterial(ResourceManager* rm, const char* fnNormals1, const char* fnNormals2, const char* fnNormals3, 12 | const char* fnNormals4, const char* fnDiff1, const char* fnDiff2, const char* fnDiff3, const char* fnDiff4, XMFLOAT4 colors[4]) : 13 | m_pResMgr(rm) { 14 | unsigned int index[8], height, width; 15 | 16 | // assumes that all files are the same size. 17 | // forces this to be true as we are building a texture2darray. 18 | index[0] = m_pResMgr->LoadFile(fnNormals1, height, width); 19 | index[1] = m_pResMgr->LoadFile(fnNormals2, height, width); 20 | index[2] = m_pResMgr->LoadFile(fnNormals3, height, width); 21 | index[3] = m_pResMgr->LoadFile(fnNormals4, height, width); 22 | index[4] = m_pResMgr->LoadFile(fnDiff1, height, width); 23 | index[5] = m_pResMgr->LoadFile(fnDiff2, height, width); 24 | index[6] = m_pResMgr->LoadFile(fnDiff3, height, width); 25 | index[7] = m_pResMgr->LoadFile(fnDiff4, height, width); 26 | 27 | // Create the texture buffers. 28 | D3D12_RESOURCE_DESC descTex = {}; 29 | descTex.MipLevels = 1; 30 | descTex.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 31 | descTex.Width = width; 32 | descTex.Height = height; 33 | descTex.Flags = D3D12_RESOURCE_FLAG_NONE; 34 | descTex.DepthOrArraySize = 8; 35 | descTex.SampleDesc.Count = 1; 36 | descTex.SampleDesc.Quality = 0; 37 | descTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 38 | 39 | ID3D12Resource* textures; 40 | unsigned int iBuffer = m_pResMgr->NewBuffer(textures, &descTex, &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 41 | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr); 42 | textures->SetName(L"Texture Array Buffer"); 43 | 44 | D3D12_SUBRESOURCE_DATA dataTex[8]; 45 | // prepare detail map data for upload. 46 | for (int i = 0; i < 8; ++i) { 47 | dataTex[i] = {}; 48 | dataTex[i].pData = m_pResMgr->GetFileData(index[i]); 49 | dataTex[i].RowPitch = width * 4 * sizeof(unsigned char); 50 | dataTex[i].SlicePitch = height * width * 4 * sizeof(unsigned char); 51 | } 52 | 53 | m_pResMgr->UploadToBuffer(iBuffer, 8, dataTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 54 | 55 | // Create the SRV for the detail map texture and save to Terrain object. 56 | D3D12_SHADER_RESOURCE_VIEW_DESC descSRV = {}; 57 | descSRV.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 58 | descSRV.Format = descTex.Format; 59 | descSRV.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; 60 | descSRV.Texture2DArray.ArraySize = descTex.DepthOrArraySize; 61 | descSRV.Texture2DArray.MipLevels = descTex.MipLevels; 62 | 63 | m_pResMgr->AddSRV(textures, &descSRV, m_hdlTextureSRV_CPU, m_hdlTextureSRV_GPU); 64 | 65 | m_listColors[0] = colors[0]; 66 | m_listColors[1] = colors[1]; 67 | m_listColors[2] = colors[2]; 68 | m_listColors[3] = colors[3]; 69 | } 70 | 71 | TerrainMaterial::~TerrainMaterial() { 72 | m_pResMgr = nullptr; 73 | } 74 | 75 | // Attach our SRV to the provided root descriptor table slot. 76 | void TerrainMaterial::Attach(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex) { 77 | cmdList->SetGraphicsRootDescriptorTable(srvDescTableIndex, m_hdlTextureSRV_GPU); 78 | } -------------------------------------------------------------------------------- /Render Terrain/Material.h: -------------------------------------------------------------------------------- 1 | /* 2 | Material.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Classes for creating and managing Direct3D 12 materials. 8 | 9 | Usage: - Proper shutdown is handled by the destructor. 10 | - Currently just a TerrainMaterial that takes 4 normal maps 11 | and 4 diffuse maps, as well as 4 colors. 12 | 13 | Future Work: - Add a more generic Material class. 14 | - Add more material properties, ie specularity. 15 | - Add methods to access material properties. 16 | */ 17 | #pragma once 18 | #include "ResourceManager.h" 19 | 20 | class TerrainMaterial { 21 | public: 22 | TerrainMaterial(ResourceManager* rm, const char* fnNormals1, const char* fnNormals2, const char* fnNormals3, 23 | const char* fnNormals4, const char* fnDiff1, const char* fnDiff2, const char* fnDiff3, const char* fnDiff4, 24 | XMFLOAT4 colors[4]); 25 | ~TerrainMaterial(); 26 | 27 | // Attach our SRV to the provided root descriptor table slot. 28 | void Attach(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex); 29 | // return the array of colors. 30 | XMFLOAT4* GetColors() { return m_listColors; } 31 | private: 32 | ResourceManager* m_pResMgr; 33 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlTextureSRV_CPU; 34 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlTextureSRV_GPU; 35 | XMFLOAT4 m_listColors[4]; 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /Render Terrain/Render Terrain.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {63E8FA05-4B12-40B9-AA5D-F0ED69215AC3} 23 | Win32Proj 24 | RenderTerrain 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v140 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v140 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v140 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v140 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | true 77 | C:\Program Files %28x86%29\Windows Kits\10\Include\10.0.10586.0\shared;C:\Program Files %28x86%29\Windows Kits\10\Include\10.0.10586.0\um;$(IncludePath) 78 | C:\Program Files %28x86%29\Windows Kits\10\Lib\10.0.10586.0\um\x64;$(LibraryPath) 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | C:\Program Files %28x86%29\Windows Kits\10\Include\10.0.10586.0\shared;C:\Program Files %28x86%29\Windows Kits\10\Include\10.0.10586.0\um;$(IncludePath) 86 | C:\Program Files %28x86%29\Windows Kits\10\Lib\10.0.10586.0\um\x64;$(LibraryPath) 87 | 88 | 89 | 90 | 91 | 92 | Level3 93 | Disabled 94 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 95 | 96 | 97 | Windows 98 | true 99 | 100 | 101 | 102 | 103 | 104 | 105 | Level3 106 | Disabled 107 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 108 | 109 | 110 | Windows 111 | true 112 | d3d12.lib;dxgi.lib;d3dcompiler.lib;%(AdditionalDependencies) 113 | 114 | 115 | true 116 | 117 | 118 | 119 | 120 | Level3 121 | 122 | 123 | MaxSpeed 124 | true 125 | true 126 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 127 | 128 | 129 | Windows 130 | true 131 | true 132 | true 133 | 134 | 135 | 136 | 137 | Level3 138 | 139 | 140 | MaxSpeed 141 | true 142 | true 143 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 144 | 145 | 146 | Windows 147 | true 148 | true 149 | true 150 | d3d12.lib;dxgi.lib;d3dcompiler.lib;%(AdditionalDependencies) 151 | 152 | 153 | true 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | true 192 | true 193 | 194 | 195 | true 196 | true 197 | 198 | 199 | Pixel 200 | Pixel 201 | Pixel 202 | Pixel 203 | true 204 | true 205 | 206 | 207 | Vertex 208 | Vertex 209 | Vertex 210 | Vertex 211 | true 212 | true 213 | 214 | 215 | Domain 216 | 5.0 217 | Domain 218 | 5.0 219 | Domain 220 | 5.0 221 | Domain 222 | 5.0 223 | true 224 | true 225 | 226 | 227 | Hull 228 | 5.0 229 | Hull 230 | 5.0 231 | Hull 232 | 5.0 233 | Hull 234 | 5.0 235 | true 236 | true 237 | 238 | 239 | true 240 | true 241 | 242 | 243 | true 244 | true 245 | 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /Render Terrain/Render Terrain.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | 109 | 110 | Resource Files 111 | 112 | 113 | Resource Files 114 | 115 | 116 | Resource Files 117 | 118 | 119 | Resource Files 120 | 121 | 122 | Resource Files 123 | 124 | 125 | Resource Files 126 | 127 | 128 | Resource Files 129 | 130 | 131 | Resource Files 132 | 133 | 134 | -------------------------------------------------------------------------------- /Render Terrain/RenderShadowMapDS.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer TerrainData : register(b0) { 2 | float scale; 3 | float width; 4 | float depth; 5 | float base; 6 | } 7 | 8 | cbuffer ShadowConstants : register(b1) { 9 | float4x4 shadowmatrix; 10 | } 11 | 12 | Texture2D heightmap : register(t0); 13 | Texture2D displacementmap : register(t1); 14 | SamplerState hmsampler : register(s0); 15 | SamplerState displacementsampler : register(s1); 16 | 17 | struct DS_OUTPUT 18 | { 19 | float4 pos : SV_POSITION; 20 | }; 21 | 22 | // Output control point 23 | struct HS_CONTROL_POINT_OUTPUT 24 | { 25 | float3 worldpos : POSITION; 26 | }; 27 | 28 | // Output patch constant data. 29 | struct HS_CONSTANT_DATA_OUTPUT 30 | { 31 | float EdgeTessFactor[4] : SV_TessFactor; // e.g. would be [4] for a quad domain 32 | float InsideTessFactor[2] : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain 33 | uint skirt : SKIRT; 34 | }; 35 | 36 | float3 estimateNormal(float2 texcoord) { 37 | float2 b = texcoord + float2(0.0f, -0.3f / depth); 38 | float2 c = texcoord + float2(0.3f / width, -0.3f / depth); 39 | float2 d = texcoord + float2(0.3f / width, 0.0f); 40 | float2 e = texcoord + float2(0.3f / width, 0.3f / depth); 41 | float2 f = texcoord + float2(0.0f, 0.3f / depth); 42 | float2 g = texcoord + float2(-0.3f / width, 0.3f / depth); 43 | float2 h = texcoord + float2(-0.3f / width, 0.0f); 44 | float2 i = texcoord + float2(-0.3f / width, -0.3f / depth); 45 | 46 | float zb = heightmap.SampleLevel(hmsampler, b, 0).x * scale; 47 | float zc = heightmap.SampleLevel(hmsampler, c, 0).x * scale; 48 | float zd = heightmap.SampleLevel(hmsampler, d, 0).x * scale; 49 | float ze = heightmap.SampleLevel(hmsampler, e, 0).x * scale; 50 | float zf = heightmap.SampleLevel(hmsampler, f, 0).x * scale; 51 | float zg = heightmap.SampleLevel(hmsampler, g, 0).x * scale; 52 | float zh = heightmap.SampleLevel(hmsampler, h, 0).x * scale; 53 | float zi = heightmap.SampleLevel(hmsampler, i, 0).x * scale; 54 | 55 | float x = zg + 2 * zh + zi - zc - 2 * zd - ze; 56 | float y = 2 * zb + zc + zi - ze - 2 * zf - zg; 57 | float z = 8.0f; 58 | 59 | return normalize(float3(x, y, z)); 60 | } 61 | 62 | #define NUM_CONTROL_POINTS 4 63 | 64 | [domain("quad")] 65 | DS_OUTPUT main( 66 | HS_CONSTANT_DATA_OUTPUT input, 67 | float2 domain : SV_DomainLocation, 68 | const OutputPatch patch) 69 | { 70 | DS_OUTPUT output; 71 | float3 worldpos = lerp(lerp(patch[0].worldpos, patch[1].worldpos, domain.x), lerp(patch[2].worldpos, patch[3].worldpos, domain.x), domain.y); 72 | 73 | if (input.skirt < 5) { 74 | if (input.skirt > 0 && domain.y == 1) { 75 | worldpos.z = heightmap.SampleLevel(hmsampler, worldpos / width, 0.0f).x * scale; 76 | } 77 | } else { 78 | worldpos.z = heightmap.SampleLevel(hmsampler, worldpos / width, 0.0f).x * scale; 79 | } 80 | 81 | float3 norm = estimateNormal(worldpos / width); 82 | worldpos += norm * 0.5f * (2.0f * displacementmap.SampleLevel(displacementsampler, worldpos / 32, 0.0f).w - 1.0f); 83 | 84 | output.pos = float4(worldpos, 1.0f); 85 | output.pos = mul(output.pos, shadowmatrix); 86 | return output; 87 | } 88 | -------------------------------------------------------------------------------- /Render Terrain/RenderShadowMapHS.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer ShadowConstants : register(b1) 2 | { 3 | float4x4 shadowmatrix; 4 | float4 eye; 5 | float4 frustum[4]; 6 | } 7 | 8 | // Input control point 9 | struct VS_OUTPUT 10 | { 11 | float3 worldpos : POSITION0; 12 | float3 aabbmin : POSITION1; 13 | float3 aabbmax : POSITION2; 14 | uint skirt : SKIRT; 15 | }; 16 | // Output control point 17 | struct HS_CONTROL_POINT_OUTPUT 18 | { 19 | float3 worldpos : POSITION; 20 | }; 21 | 22 | // Output patch constant data. 23 | struct HS_CONSTANT_DATA_OUTPUT 24 | { 25 | float EdgeTessFactor[4] : SV_TessFactor; // e.g. would be [4] for a quad domain 26 | float InsideTessFactor[2] : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain 27 | uint skirt : SKIRT; 28 | }; 29 | 30 | #define NUM_CONTROL_POINTS 4 31 | 32 | // returns true if the box is completely behind the plane. 33 | bool aabbBehindPlaneTest(float3 center, float3 extents, float4 plane) { 34 | float3 n = abs(plane.xyz); // |n.x|, |n.y|, |n.z| 35 | 36 | float e = dot(extents, n); // always positive 37 | 38 | float s = dot(float4(center, 1.0f), plane); // signed distance from center point to plane 39 | 40 | // if the center point of the box is a distance of e or more behind the plane 41 | // (in which case s is negative since it is behind the plane), then the box 42 | // is completely in the negative half space of the plane. 43 | return (s + e) < 0.0f; 44 | } 45 | 46 | // returns true if the box is completely outside the frustum. 47 | bool aabbOutsideFrustumTest(float3 center, float3 extents, float4 frustumPlanes[4]) { 48 | [unroll] 49 | for (int i = 0; i < 4; ++i) { 50 | // if the box is completely behind any of the frustum planes, then it is outside the frustum. 51 | if (aabbBehindPlaneTest(center, extents, frustumPlanes[i])) { 52 | return true; 53 | } 54 | } 55 | 56 | return false; 57 | } 58 | 59 | float CalcTessFactor(float3 p) { 60 | float d = distance(p, eye); 61 | 62 | float s = saturate((d - 128.0f) / (256.0f - 128.0f)); 63 | return pow(2, (lerp(4, 0, s))); 64 | } 65 | 66 | // Patch Constant Function 67 | HS_CONSTANT_DATA_OUTPUT CalcHSPatchConstants( 68 | InputPatch ip, 69 | uint PatchID : SV_PrimitiveID) 70 | { 71 | HS_CONSTANT_DATA_OUTPUT output; 72 | output.skirt = ip[0].skirt; 73 | 74 | // build axis-aligned bounding box. 75 | // ip[0] is lower left corner 76 | // ip[3] is upper right corner 77 | // get z value from boundsZ variable. Correct value will be in ip[0]. 78 | float3 vMin = ip[0].aabbmin; 79 | float3 vMax = ip[0].aabbmax; 80 | 81 | // center/extents representation. 82 | float3 boxCenter = 0.5f * (vMin + vMax); 83 | float3 boxExtents = 0.5f * (vMax - vMin); 84 | 85 | if (aabbOutsideFrustumTest(boxCenter, boxExtents, frustum)) { 86 | output.EdgeTessFactor[0] = 0.0f; 87 | output.EdgeTessFactor[1] = 0.0f; 88 | output.EdgeTessFactor[2] = 0.0f; 89 | output.EdgeTessFactor[3] = 0.0f; 90 | output.InsideTessFactor[0] = 0.0f; 91 | output.InsideTessFactor[1] = 0.0f; 92 | 93 | return output; 94 | } 95 | else { 96 | // don't tessellate any part of the skirt that isn't directly touching the terrain. 97 | if (output.skirt == 0) { 98 | output.EdgeTessFactor[0] = 1.0f; 99 | output.EdgeTessFactor[1] = 1.0f; 100 | output.EdgeTessFactor[2] = 1.0f; 101 | output.EdgeTessFactor[3] = 1.0f; 102 | output.InsideTessFactor[0] = 1.0f; 103 | output.InsideTessFactor[1] = 1.0f; 104 | 105 | return output; 106 | } 107 | else if (output.skirt < 5) { 108 | float3 e3 = 0.5f * (ip[2].worldpos + ip[3].worldpos); 109 | 110 | output.EdgeTessFactor[0] = 1.0f; 111 | output.EdgeTessFactor[1] = 1.0f; 112 | output.EdgeTessFactor[2] = 1.0f; 113 | output.EdgeTessFactor[3] = CalcTessFactor(e3); 114 | output.InsideTessFactor[0] = 1.0f; 115 | output.InsideTessFactor[1] = 1.0f; 116 | 117 | return output; 118 | } 119 | 120 | // tessellate based on distance from the camera. 121 | // compute tess factor based on edges. 122 | // compute midpoint of edges. 123 | float3 e0 = 0.5f * (ip[0].worldpos + ip[2].worldpos); 124 | float3 e1 = 0.5f * (ip[0].worldpos + ip[1].worldpos); 125 | float3 e2 = 0.5f * (ip[1].worldpos + ip[3].worldpos); 126 | float3 e3 = 0.5f * (ip[2].worldpos + ip[3].worldpos); 127 | float3 c = 0.25f * (ip[0].worldpos + ip[1].worldpos + ip[2].worldpos + ip[3].worldpos); 128 | 129 | output.EdgeTessFactor[0] = CalcTessFactor(e0); 130 | output.EdgeTessFactor[1] = CalcTessFactor(e1); 131 | output.EdgeTessFactor[2] = CalcTessFactor(e2); 132 | output.EdgeTessFactor[3] = CalcTessFactor(e3); 133 | output.InsideTessFactor[0] = CalcTessFactor(c); 134 | output.InsideTessFactor[1] = output.InsideTessFactor[0]; 135 | 136 | return output; 137 | } 138 | } 139 | 140 | [domain("quad")] 141 | [partitioning("fractional_even")] 142 | [outputtopology("triangle_cw")] 143 | [outputcontrolpoints(4)] 144 | [patchconstantfunc("CalcHSPatchConstants")] 145 | HS_CONTROL_POINT_OUTPUT main( 146 | InputPatch ip, 147 | uint i : SV_OutputControlPointID, 148 | uint PatchID : SV_PrimitiveID ) 149 | { 150 | HS_CONTROL_POINT_OUTPUT output; 151 | 152 | // Insert code to compute Output here 153 | output.worldpos = ip[i].worldpos; 154 | 155 | return output; 156 | } 157 | -------------------------------------------------------------------------------- /Render Terrain/RenderTerrain2dPS.hlsl: -------------------------------------------------------------------------------- 1 | Texture2D heightmap : register(t0); 2 | SamplerState hmsampler : register(s0); 3 | 4 | struct VS_OUTPUT { 5 | float4 pos : SV_POSITION; 6 | float2 tex : TEXCOORD; 7 | }; 8 | 9 | float4 main(VS_OUTPUT input) : SV_TARGET { 10 | return heightmap.Sample(hmsampler, input.tex); 11 | } -------------------------------------------------------------------------------- /Render Terrain/RenderTerrain2dVS.hlsl: -------------------------------------------------------------------------------- 1 | struct VS_OUTPUT { 2 | float4 pos : SV_POSITION; 3 | float2 tex : TEXCOORD; 4 | }; 5 | 6 | VS_OUTPUT main(uint input : SV_VERTEXID) { 7 | VS_OUTPUT output; 8 | 9 | output.pos = float4(float2((input << 1) & 2, input == 0) * float2(2.0f, -4.0f) + float2(-1.0f, 1.0f), 0.0f, 1.0f); 10 | output.tex = float2((output.pos.x + 1) / 2, (output.pos.y + 1) / 2); 11 | 12 | return output; 13 | } -------------------------------------------------------------------------------- /Render Terrain/RenderTerrainTessDS.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer TerrainData : register(b0) 2 | { 3 | float scale; 4 | float width; 5 | float depth; 6 | float base; 7 | } 8 | 9 | cbuffer PerFrameData : register(b1) 10 | { 11 | float4x4 viewproj; 12 | float4x4 shadowtexmatrices[4]; 13 | float4 eye; 14 | float4 frustum[6]; 15 | } 16 | 17 | Texture2D heightmap : register(t0); 18 | Texture2D displacementmap : register(t1); 19 | 20 | SamplerState hmsampler : register(s0); 21 | SamplerState detailsampler : register(s1); 22 | SamplerState displacementsampler : register(s3); 23 | 24 | struct DS_OUTPUT 25 | { 26 | float4 pos : SV_POSITION; 27 | float4 shadowpos[4] : TEXCOORD0; 28 | float3 worldpos : POSITION; 29 | }; 30 | 31 | // Output control point 32 | struct HS_CONTROL_POINT_OUTPUT 33 | { 34 | float3 worldpos : POSITION; 35 | }; 36 | 37 | // Output patch constant data. 38 | struct HS_CONSTANT_DATA_OUTPUT 39 | { 40 | float EdgeTessFactor[4] : SV_TessFactor; // e.g. would be [4] for a quad domain 41 | float InsideTessFactor[2] : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain 42 | uint skirt : SKIRT; 43 | }; 44 | 45 | #define NUM_CONTROL_POINTS 4 46 | 47 | float3 estimateNormal(float2 texcoord) { 48 | float2 b = texcoord + float2(0.0f, -0.3f / depth); 49 | float2 c = texcoord + float2(0.3f / width, -0.3f / depth); 50 | float2 d = texcoord + float2(0.3f / width, 0.0f); 51 | float2 e = texcoord + float2(0.3f / width, 0.3f / depth); 52 | float2 f = texcoord + float2(0.0f, 0.3f / depth); 53 | float2 g = texcoord + float2(-0.3f / width, 0.3f / depth); 54 | float2 h = texcoord + float2(-0.3f / width, 0.0f); 55 | float2 i = texcoord + float2(-0.3f / width, -0.3f / depth); 56 | 57 | float zb = heightmap.SampleLevel(hmsampler, b, 0).x * scale; 58 | float zc = heightmap.SampleLevel(hmsampler, c, 0).x * scale; 59 | float zd = heightmap.SampleLevel(hmsampler, d, 0).x * scale; 60 | float ze = heightmap.SampleLevel(hmsampler, e, 0).x * scale; 61 | float zf = heightmap.SampleLevel(hmsampler, f, 0).x * scale; 62 | float zg = heightmap.SampleLevel(hmsampler, g, 0).x * scale; 63 | float zh = heightmap.SampleLevel(hmsampler, h, 0).x * scale; 64 | float zi = heightmap.SampleLevel(hmsampler, i, 0).x * scale; 65 | 66 | float x = zg + 2 * zh + zi - zc - 2 * zd - ze; 67 | float y = 2 * zb + zc + zi - ze - 2 * zf - zg; 68 | float z = 8.0f; 69 | 70 | return normalize(float3(x, y, z)); 71 | } 72 | 73 | [domain("quad")] 74 | DS_OUTPUT main( 75 | HS_CONSTANT_DATA_OUTPUT input, 76 | float2 domain : SV_DomainLocation, 77 | const OutputPatch patch) 78 | { 79 | DS_OUTPUT output; 80 | 81 | output.worldpos = lerp(lerp(patch[0].worldpos, patch[1].worldpos, domain.x), lerp(patch[2].worldpos, patch[3].worldpos, domain.x), domain.y); 82 | 83 | float h; 84 | if (input.skirt < 5) { 85 | if (input.skirt > 0 && domain.y == 1) { 86 | h = heightmap.SampleLevel(hmsampler, output.worldpos / width, 0.0f).x; 87 | output.worldpos.z = h * scale; 88 | } 89 | } else { 90 | h = heightmap.SampleLevel(hmsampler, output.worldpos / width, 0.0f).x; 91 | output.worldpos.z = h * scale; 92 | } 93 | 94 | float3 norm = estimateNormal(output.worldpos / width); 95 | output.worldpos += norm * 0.5f * (2.0f * displacementmap.SampleLevel(displacementsampler, output.worldpos / 32, 0.0f).w - 1.0f); 96 | 97 | // generate coordinates transformed into view/projection space. 98 | output.pos = float4(output.worldpos, 1.0f); 99 | output.pos = mul(output.pos, viewproj); 100 | 101 | [unroll] 102 | for (int i = 0; i < 4; ++i) { 103 | // generate projective tex-coords to project shadow map onto scene. 104 | output.shadowpos[i] = float4(output.worldpos, 1.0f); 105 | 106 | output.shadowpos[i] = mul(output.shadowpos[i], shadowtexmatrices[i]); 107 | } 108 | return output; 109 | } 110 | -------------------------------------------------------------------------------- /Render Terrain/RenderTerrainTessHS.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer PerFrameData : register(b1) 2 | { 3 | float4x4 viewproj; 4 | float4x4 shadowtexmatrices[4]; 5 | float4 eye; 6 | float4 frustum[6]; 7 | } 8 | 9 | // Input control point 10 | struct VS_OUTPUT 11 | { 12 | float3 worldpos : POSITION0; 13 | float3 aabbmin : POSITION1; 14 | float3 aabbmax : POSITION2; 15 | uint skirt : SKIRT; 16 | }; 17 | // Output control point 18 | struct HS_CONTROL_POINT_OUTPUT 19 | { 20 | float3 worldpos : POSITION; 21 | }; 22 | 23 | // Output patch constant data. 24 | struct HS_CONSTANT_DATA_OUTPUT 25 | { 26 | float EdgeTessFactor[4] : SV_TessFactor; // e.g. would be [4] for a quad domain 27 | float InsideTessFactor[2] : SV_InsideTessFactor; // e.g. would be Inside[2] for a quad domain 28 | uint skirt : SKIRT; 29 | }; 30 | 31 | #define NUM_CONTROL_POINTS 4 32 | 33 | // code for frustum culling and dynamic LOD from Introduction to 3D Game Programming with DirectX 11. 34 | 35 | // returns true if the box is completely behind the plane. 36 | bool aabbBehindPlaneTest(float3 center, float3 extents, float4 plane) { 37 | float3 n = abs(plane.xyz); // |n.x|, |n.y|, |n.z| 38 | 39 | float e = dot(extents, n); // always positive 40 | 41 | float s = dot(float4(center, 1.0f), plane); // signed distance from center point to plane 42 | 43 | // if the center point of the box is a distance of e or more behind the plane 44 | // (in which case s is negative since it is behind the plane), then the box 45 | // is completely in the negative half space of the plane. 46 | return (s + e) < 0.0f; 47 | } 48 | 49 | // returns true if the box is completely outside the frustum. 50 | bool aabbOutsideFrustumTest(float3 center, float3 extents, float4 frustumPlanes[6]) { 51 | [unroll] 52 | for (int i = 0; i < 6; ++i) { 53 | // if the box is completely behind any of the frustum planes, then it is outside the frustum. 54 | if (aabbBehindPlaneTest(center, extents, frustumPlanes[i])) { 55 | return true; 56 | } 57 | } 58 | 59 | return false; 60 | } 61 | 62 | float CalcTessFactor(float3 p) { 63 | float d = distance(p, eye.xyz); 64 | 65 | float s = saturate((d - 16.0f) / (256.0f - 16.0f)); 66 | return pow(2, (lerp(6, 0, s))); 67 | } 68 | 69 | // Patch Constant Function 70 | HS_CONSTANT_DATA_OUTPUT CalcHSPatchConstants( 71 | InputPatch ip, 72 | uint PatchID : SV_PrimitiveID) 73 | { 74 | HS_CONSTANT_DATA_OUTPUT output; 75 | output.skirt = ip[0].skirt; 76 | 77 | // build axis-aligned bounding box. 78 | float3 vMin = ip[0].aabbmin; 79 | float3 vMax = ip[0].aabbmax; 80 | 81 | // center/extents representation. 82 | float3 boxCenter = 0.5f * (vMin + vMax); 83 | float3 boxExtents = 0.5f * (vMax - vMin); 84 | 85 | if (aabbOutsideFrustumTest(boxCenter, boxExtents, frustum)) { 86 | output.EdgeTessFactor[0] = 0.0f; 87 | output.EdgeTessFactor[1] = 0.0f; 88 | output.EdgeTessFactor[2] = 0.0f; 89 | output.EdgeTessFactor[3] = 0.0f; 90 | output.InsideTessFactor[0] = 0.0f; 91 | output.InsideTessFactor[1] = 0.0f; 92 | 93 | return output; 94 | } else { 95 | // don't tessellate any part of the skirt that isn't directly touching the terrain. 96 | if (output.skirt == 0) { 97 | output.EdgeTessFactor[0] = 1.0f; 98 | output.EdgeTessFactor[1] = 1.0f; 99 | output.EdgeTessFactor[2] = 1.0f; 100 | output.EdgeTessFactor[3] = 1.0f; 101 | output.InsideTessFactor[0] = 1.0f; 102 | output.InsideTessFactor[1] = 1.0f; 103 | 104 | return output; 105 | } else if (output.skirt < 5) { 106 | float3 e3 = 0.5f * (ip[2].worldpos + ip[3].worldpos); 107 | 108 | output.EdgeTessFactor[0] = 1.0f; 109 | output.EdgeTessFactor[1] = 1.0f; 110 | output.EdgeTessFactor[2] = 1.0f; 111 | output.EdgeTessFactor[3] = CalcTessFactor(e3); 112 | output.InsideTessFactor[0] = 1.0f; 113 | output.InsideTessFactor[1] = 1.0f; 114 | 115 | return output; 116 | } 117 | // tessellate based on distance from the camera. 118 | // compute tess factor based on edges. 119 | // compute midpoint of edges. 120 | float3 e0 = 0.5f * (ip[0].worldpos + ip[2].worldpos); 121 | float3 e1 = 0.5f * (ip[0].worldpos + ip[1].worldpos); 122 | float3 e2 = 0.5f * (ip[1].worldpos + ip[3].worldpos); 123 | float3 e3 = 0.5f * (ip[2].worldpos + ip[3].worldpos); 124 | float3 c = 0.25f * (ip[0].worldpos + ip[1].worldpos + ip[2].worldpos + ip[3].worldpos); 125 | 126 | output.EdgeTessFactor[0] = CalcTessFactor(e0); 127 | output.EdgeTessFactor[1] = CalcTessFactor(e1); 128 | output.EdgeTessFactor[2] = CalcTessFactor(e2); 129 | output.EdgeTessFactor[3] = CalcTessFactor(e3); 130 | output.InsideTessFactor[0] = CalcTessFactor(c); 131 | output.InsideTessFactor[1] = output.InsideTessFactor[0]; 132 | 133 | return output; 134 | } 135 | } 136 | 137 | [domain("quad")] 138 | [partitioning("fractional_even")] 139 | [outputtopology("triangle_cw")] 140 | [outputcontrolpoints(4)] 141 | [patchconstantfunc("CalcHSPatchConstants")] 142 | HS_CONTROL_POINT_OUTPUT main( 143 | InputPatch ip, 144 | uint i : SV_OutputControlPointID, 145 | uint PatchID : SV_PrimitiveID ) 146 | { 147 | HS_CONTROL_POINT_OUTPUT output; 148 | 149 | // Insert code to compute Output here 150 | output.worldpos = ip[i].worldpos; 151 | 152 | return output; 153 | } 154 | -------------------------------------------------------------------------------- /Render Terrain/RenderTerrainTessPS.hlsl: -------------------------------------------------------------------------------- 1 | struct LightData { 2 | float4 pos; 3 | float4 amb; 4 | float4 dif; 5 | float4 spec; 6 | float3 att; 7 | float rng; 8 | float3 dir; 9 | float sexp; 10 | }; 11 | 12 | cbuffer TerrainData : register(b0) 13 | { 14 | float scale; 15 | float width; 16 | float depth; 17 | float base; 18 | } 19 | 20 | cbuffer PerFrameData : register(b1) 21 | { 22 | float4x4 viewproj; 23 | float4x4 shadowtexmatrices[4]; 24 | float4 eye; 25 | float4 frustum[6]; 26 | LightData light; 27 | bool useTextures; 28 | } 29 | 30 | Texture2D heightmap : register(t0); 31 | Texture2D displacementmap : register(t1); 32 | Texture2D shadowmap : register(t2); 33 | Texture2DArray detailmaps : register(t3); 34 | 35 | SamplerState hmsampler : register(s0); 36 | SamplerComparisonState shadowsampler : register(s2); 37 | SamplerState displacementsampler : register(s3); 38 | 39 | struct DS_OUTPUT 40 | { 41 | float4 pos : SV_POSITION; 42 | float4 shadowpos[4] : TEXCOORD0; 43 | float3 worldpos : POSITION; 44 | }; 45 | 46 | // shadow map constants 47 | static const float SMAP_SIZE = 4096.0f; 48 | static const float SMAP_DX = 1.0f / SMAP_SIZE; 49 | static const float4 colors[] = { { 0.35f, 0.5f, 0.18f, 1.0f },{ 0.89f, 0.89f, 0.89f, 1.0f },{ 0.31f, 0.25f, 0.2f, 1.0f },{ 0.39f, 0.37f, 0.38f, 1.0f } }; 50 | 51 | // code for putting together cotangent frame and perturbing normal from normal map. 52 | // code originally presented by Christian Schuler 53 | // http://www.thetenthplanet.de/archives/1180 54 | // converted from his glsl to hlsl 55 | float3x3 cotangent_frame(float3 N, float3 p, float2 uv) { 56 | // get edge vectors of the pixel triangle 57 | float3 dp1 = ddx(p); 58 | float3 dp2 = ddy(p); 59 | float2 duv1 = ddx(uv); 60 | float2 duv2 = ddy(uv); 61 | 62 | // solve the linear system 63 | float3 dp2perp = cross(dp2, N); 64 | float3 dp1perp = cross(N, dp1); 65 | float3 T = dp2perp * duv1.x + dp1perp * duv2.x; 66 | float3 B = dp2perp * duv1.y + dp1perp * duv2.y; 67 | 68 | // construct a scale-invariant frame 69 | float invmax = rsqrt(max(dot(T, T), dot(B, B))); 70 | return float3x3(T * invmax, B * invmax, N); 71 | } 72 | 73 | float3 perturb_normal(float3 N, float3 V, float2 texcoord, Texture2D tex, SamplerState sam) { 74 | // assume N, the interpolated vertex normal and 75 | // V, the view vector (vertex to eye) 76 | float3 map = 2.0f * tex.Sample(sam, texcoord.xy).xyz - 1.0f; 77 | map.z *= 2.0f; // scale normal as displacement height is scaled by 0.5 78 | float3x3 TBN = cotangent_frame(N, -V, texcoord.xy); 79 | return normalize(mul(map, TBN)); 80 | } 81 | 82 | float4 SampleDetailTriplanar(float3 uvw, float3 N, float index) { 83 | float tighten = 0.4679f; 84 | float3 blending = saturate(abs(N) - tighten); 85 | // force weights to sum to 1.0 86 | float b = blending.x + blending.y + blending.z; 87 | blending /= float3(b, b, b); 88 | 89 | float4 x = detailmaps.Sample(displacementsampler, float3(uvw.yz, index)); 90 | float4 y = detailmaps.Sample(displacementsampler, float3(uvw.xz, index)); 91 | float4 z = detailmaps.Sample(displacementsampler, float3(uvw.xy, index)); 92 | 93 | return x * blending.x + y * blending.y + z * blending.z; 94 | } 95 | 96 | float4 Blend(float4 tex1, float blend1, float4 tex2, float blend2) { 97 | float depth = 0.2f; 98 | 99 | float ma = max(tex1.a + blend1, tex2.a + blend2) - depth; 100 | 101 | float b1 = max(tex1.a + blend1 - ma, 0); 102 | float b2 = max(tex2.a + blend2 - ma, 0); 103 | 104 | return (tex1 * b1 + tex2 * b2) / (b1 + b2); 105 | } 106 | 107 | float4 GetTexByHeightPlanar(float height, float3 uvw, float low, float med, float high) { 108 | float bounds = scale * 0.005f; 109 | float transition = scale * 0.6f; 110 | float lowBlendStart = transition - 2 * bounds; 111 | float highBlendEnd = transition + 2 * bounds; 112 | float4 c; 113 | 114 | if (height < lowBlendStart) { 115 | c = detailmaps.Sample(displacementsampler, float3(uvw.xy, low)); 116 | } else if (height < transition) { 117 | float4 c1 = detailmaps.Sample(displacementsampler, float3(uvw.xy, low)); 118 | float4 c2 = detailmaps.Sample(displacementsampler, float3(uvw.xy, med)); 119 | 120 | float blend = (height - lowBlendStart) * (1.0f / (transition - lowBlendStart)); 121 | 122 | c = Blend(c1, 1 - blend, c2, blend); 123 | } else if (height < highBlendEnd) { 124 | float4 c1 = detailmaps.Sample(displacementsampler, float3(uvw.xy, med)); 125 | float4 c2 = detailmaps.Sample(displacementsampler, float3(uvw.xy, high)); 126 | 127 | float blend = (height - transition) * (1.0f / (highBlendEnd - transition)); 128 | 129 | c = Blend(c1, 1 - blend, c2, blend); 130 | } else { 131 | c = detailmaps.Sample(displacementsampler, float3(uvw.xy, high)); 132 | } 133 | 134 | return c; 135 | } 136 | 137 | float4 GetTexByHeightTriplanar(float height, float3 uvw, float3 N, float index1, float index2) { 138 | float bounds = scale * 0.005f; 139 | float transition = scale * 0.6f; 140 | float blendStart = transition - bounds; 141 | float blendEnd = transition + bounds; 142 | float4 c; 143 | 144 | if (height < blendStart) { 145 | c = SampleDetailTriplanar(uvw, N, index1); 146 | } else if (height < blendEnd) { 147 | float4 c1 = SampleDetailTriplanar(uvw, N, index1); 148 | float4 c2 = SampleDetailTriplanar(uvw, N, index2); 149 | float blend = (height - blendStart) * (1.0f / (blendEnd - blendStart)); 150 | 151 | c = Blend(c1, 1 - blend, c2, blend); 152 | } else { 153 | c = SampleDetailTriplanar(uvw, N, index2); 154 | } 155 | 156 | return c; 157 | } 158 | 159 | float4 GetColorByHeight(float height, float low, float med, float high) { 160 | float bounds = scale * 0.005f; 161 | float transition = scale * 0.6f; 162 | float lowBlendStart = transition - 2 * bounds; 163 | float highBlendEnd = transition + 2 * bounds; 164 | float4 c; 165 | 166 | if (height < lowBlendStart) { 167 | c = colors[low]; 168 | } else if (height < transition) { 169 | float4 c1 = colors[low]; 170 | float4 c2 = colors[med]; 171 | 172 | float blend = (height - lowBlendStart) * (1.0f / (transition - lowBlendStart)); 173 | 174 | c = lerp(c1, c2, blend); 175 | } else if (height < highBlendEnd) { 176 | float4 c1 = colors[med]; 177 | float4 c2 = colors[high]; 178 | 179 | float blend = (height - transition) * (1.0f / (highBlendEnd - transition)); 180 | 181 | c = lerp(c1, c2, blend); 182 | } else { 183 | c = colors[high]; 184 | } 185 | 186 | return c; 187 | } 188 | 189 | float3 GetTexBySlope(float slope, float height, float3 N, float3 uvw, float startingIndex) { 190 | float4 c; 191 | float blend; 192 | if (slope < 0.6f) { 193 | blend = slope / 0.6f; 194 | float4 c1 = GetTexByHeightPlanar(height, uvw, 0 + startingIndex, 3 + startingIndex, 1 + startingIndex); 195 | float4 c2 = GetTexByHeightTriplanar(height, uvw, N, 2 + startingIndex, 3 + startingIndex); 196 | c = Blend(c1, 1 - blend, c2, blend); 197 | } else if (slope < 0.65f) { 198 | blend = (slope - 0.6f) * (1.0f / (0.65f - 0.6f)); 199 | float4 c1 = GetTexByHeightTriplanar(height, uvw, N, 2 + startingIndex, 3 + startingIndex); 200 | float4 c2 = SampleDetailTriplanar(uvw, N, 3 + startingIndex); 201 | c = Blend(c1, 1 - blend, c2, blend); 202 | } else { 203 | c = SampleDetailTriplanar(uvw, N, 3 + startingIndex); 204 | } 205 | 206 | return c.rgb; 207 | } 208 | 209 | float4 GetColorBySlope(float slope, float height) { 210 | float4 c; 211 | float blend; 212 | if (slope < 0.6f) { 213 | blend = slope / 0.6f; 214 | float4 c1 = GetColorByHeight(height, 0, 3, 1); 215 | float4 c2 = GetColorByHeight(height, 2, 3, 3); 216 | c = lerp(c1, c2, blend); 217 | } else if (slope < 0.65f) { 218 | blend = (slope - 0.6f) * (1.0f / (0.65f - 0.6f)); 219 | float4 c1 = GetColorByHeight(height, 2, 3, 3); 220 | float4 c2 = colors[3]; 221 | c = lerp(c1, c2, blend); 222 | } else { 223 | c = colors[3]; 224 | } 225 | 226 | return c; 227 | } 228 | 229 | float3 PerturbNormalByHeightSlope(float height, float slope, float3 N, float3 V, float3 uvw) { 230 | float3 c = GetTexBySlope(slope, height, N, uvw, 0) - 0.5f; 231 | 232 | float3x3 TBN = cotangent_frame(N, -V, uvw); 233 | return normalize(mul(c, TBN)); 234 | } 235 | 236 | float4 dist_based_texturing(float height, float slope, float3 N, float3 V, float3 uvw) { 237 | float dist = length(V); 238 | 239 | if (dist > 75) return GetColorBySlope(slope, height); 240 | else if (dist > 25) { 241 | float blend = (dist - 25.0f) * (1.0f / (75.0f - 25.0f)); 242 | float4 c1 = float4(GetTexBySlope(slope, height, N, uvw, 4), 1); 243 | float4 c2 = GetColorBySlope(slope, height); 244 | return lerp(c1, c2, blend); 245 | } else return float4(GetTexBySlope(slope, height, N, uvw, 4), 1); 246 | } 247 | 248 | float3 dist_based_normal(float height, float slope, float3 N, float3 V, float3 uvw) { 249 | float dist = length(V); 250 | 251 | float3 N1 = perturb_normal(N, V, uvw / 16, displacementmap, displacementsampler); 252 | 253 | if (dist > 150) return N; 254 | 255 | if (dist > 100) { 256 | float blend = (dist - 100.0f) / 50.0f; 257 | 258 | return lerp(N1, N, blend); 259 | } 260 | 261 | float3 N2 = PerturbNormalByHeightSlope(height, slope, N1, V, uvw); 262 | 263 | if (dist > 50) return N1; 264 | 265 | if (dist > 25) { 266 | float blend = (dist - 25.0f) / 25.0f; 267 | 268 | return lerp(N2, N1, blend); 269 | } 270 | 271 | return N2; 272 | } 273 | 274 | float3 estimateNormal(float2 texcoord) { 275 | float2 b = texcoord + float2(0.0f, -0.3f / depth); 276 | float2 c = texcoord + float2(0.3f / width, -0.3f / depth); 277 | float2 d = texcoord + float2(0.3f / width, 0.0f); 278 | float2 e = texcoord + float2(0.3f / width, 0.3f / depth); 279 | float2 f = texcoord + float2(0.0f, 0.3f / depth); 280 | float2 g = texcoord + float2(-0.3f / width, 0.3f / depth); 281 | float2 h = texcoord + float2(-0.3f / width, 0.0f); 282 | float2 i = texcoord + float2(-0.3f / width, -0.3f / depth); 283 | 284 | float zb = heightmap.SampleLevel(hmsampler, b, 0).x * scale; 285 | float zc = heightmap.SampleLevel(hmsampler, c, 0).x * scale; 286 | float zd = heightmap.SampleLevel(hmsampler, d, 0).x * scale; 287 | float ze = heightmap.SampleLevel(hmsampler, e, 0).x * scale; 288 | float zf = heightmap.SampleLevel(hmsampler, f, 0).x * scale; 289 | float zg = heightmap.SampleLevel(hmsampler, g, 0).x * scale; 290 | float zh = heightmap.SampleLevel(hmsampler, h, 0).x * scale; 291 | float zi = heightmap.SampleLevel(hmsampler, i, 0).x * scale; 292 | 293 | float x = zg + 2 * zh + zi - zc - 2 * zd - ze; 294 | float y = 2 * zb + zc + zi - ze - 2 * zf - zg; 295 | float z = 8.0f; 296 | 297 | return normalize(float3(x, y, z)); 298 | } 299 | 300 | float calcShadowFactor(float4 shadowPosH) { 301 | // No need to divide shadowPosH.xyz by shadowPosH.w because we only have a directional light. 302 | 303 | // Depth in NDC space. 304 | float depth = shadowPosH.z; 305 | 306 | // Texel size. 307 | const float dx = SMAP_DX; 308 | 309 | float percentLit = 0.0f; 310 | 311 | const float2 offsets[9] = { 312 | float2(-dx, -dx), float2(0.0f, -dx), float2(dx, -dx), 313 | float2(-dx, 0.0f), float2(0.0f, 0.0f), float2(dx, 0.0f), 314 | float2(-dx, dx), float2(0.0f, dx), float2(dx, dx) 315 | }; 316 | 317 | // 3x3 box filter pattern. Each sample does a 4-tap PCF. 318 | [unroll] 319 | for (int i = 0; i < 9; ++i) { 320 | percentLit += shadowmap.SampleCmpLevelZero(shadowsampler, shadowPosH.xy + offsets[i], depth); 321 | } 322 | 323 | // average the samples. 324 | return percentLit / 9.0f; 325 | } 326 | 327 | float decideOnCascade(float4 shadowpos[4]) { 328 | // if shadowpos[0].xy is in the range [0, 0.5], then this point is in the first cascade 329 | if (max(abs(shadowpos[0].x - 0.25), abs(shadowpos[0].y - 0.25)) < 0.247) { 330 | return calcShadowFactor(shadowpos[0]); 331 | } 332 | 333 | if (max(abs(shadowpos[1].x - 0.25), abs(shadowpos[1].y - 0.75)) < 0.247) { 334 | return calcShadowFactor(shadowpos[1]); 335 | } 336 | 337 | if (max(abs(shadowpos[2].x - 0.75), abs(shadowpos[2].y - 0.25)) < 0.247) { 338 | return calcShadowFactor(shadowpos[2]); 339 | } 340 | 341 | return calcShadowFactor(shadowpos[3]); 342 | } 343 | 344 | // basic diffuse/ambient lighting 345 | float4 main(DS_OUTPUT input) : SV_TARGET 346 | { 347 | float3 norm = estimateNormal(input.worldpos / width); 348 | float3 viewvector = eye.xyz - input.worldpos; 349 | 350 | norm = dist_based_normal(input.worldpos.z, acos(norm.z), norm, viewvector, input.worldpos / 2); 351 | float4 color; 352 | if (useTextures) color = dist_based_texturing(input.worldpos.z, acos(norm.z), norm, viewvector, input.worldpos / 2); 353 | else color = GetColorBySlope(acos(norm.z), input.worldpos.z); 354 | 355 | float shadowfactor = decideOnCascade(input.shadowpos); 356 | float4 diffuse = max(shadowfactor, light.amb) * light.dif * dot(-light.dir, norm); 357 | float3 V = reflect(light.dir, norm); 358 | float3 toEye = normalize(eye.xyz - input.worldpos); 359 | float4 specular = shadowfactor * 0.1f * light.spec * pow(max(dot(V, toEye), 0.0f), 2.0f); 360 | 361 | return (diffuse + specular) * color; 362 | } -------------------------------------------------------------------------------- /Render Terrain/RenderTerrainTessVS.hlsl: -------------------------------------------------------------------------------- 1 | struct VS_OUTPUT 2 | { 3 | float3 worldpos : POSITION0; 4 | float3 aabbmin : POSITION1; 5 | float3 aabbmax : POSITION2; 6 | uint skirt : SKIRT; 7 | }; 8 | 9 | struct VS_INPUT { 10 | float3 pos : POSITION0; 11 | float3 aabbmin : POSITION1; 12 | float3 aabbmax : POSITION2; 13 | uint skirt : SKIRT; 14 | }; 15 | 16 | VS_OUTPUT main(VS_INPUT input) { 17 | VS_OUTPUT output; 18 | 19 | output.worldpos = input.pos; 20 | output.aabbmin = input.aabbmin; 21 | output.aabbmax = input.aabbmax; 22 | output.skirt = input.skirt; 23 | 24 | return output; 25 | } -------------------------------------------------------------------------------- /Render Terrain/ResourceManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ResourceManager.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating and managing a Direct3D 12 Resource Maanager. 8 | */ 9 | 10 | #include "ResourceManager.h" 11 | #include "lodepng.h" 12 | #include 13 | 14 | ResourceManager::ResourceManager(Device* d, unsigned int numRTVs, unsigned int numDSVs, unsigned int numCBVSRVUAVs, 15 | unsigned int numSamplers) : m_pDev(d), m_numRTVs(numRTVs), m_numDSVs(numDSVs), m_numCBVSRVUAVs(numCBVSRVUAVs), 16 | m_numSamplers(numSamplers) { 17 | m_pheapRTV = nullptr; 18 | m_pheapDSV = nullptr; 19 | m_pheapCBVSRVUAV = nullptr; 20 | m_pheapSampler = nullptr; 21 | m_pCmdAllocator = nullptr; 22 | m_pCmdList = nullptr; 23 | m_pFence = nullptr; 24 | 25 | m_pDev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, m_pCmdAllocator); 26 | m_pDev->CreateGraphicsCommandList(D3D12_COMMAND_LIST_TYPE_DIRECT, m_pCmdAllocator, m_pCmdList); 27 | m_pCmdList->Close(); 28 | 29 | m_valFence = 0; 30 | m_pDev->CreateFence(m_valFence, D3D12_FENCE_FLAG_NONE, m_pFence); 31 | m_hdlFenceEvent = CreateEvent(NULL, false, false, NULL); 32 | if (!m_hdlFenceEvent) { 33 | throw GFX_Exception("ResourceManager::ResourceManager: Create Fence Event failed on init."); 34 | } 35 | 36 | // initialize the descriptor heaps. 37 | D3D12_DESCRIPTOR_HEAP_DESC descHeap = {}; 38 | descHeap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 39 | if (m_numRTVs) { 40 | descHeap.NumDescriptors = m_numRTVs; 41 | descHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; 42 | m_pDev->CreateDescriptorHeap(&descHeap, m_pheapRTV); 43 | m_pheapRTV->SetName(L"RTV Heap"); 44 | m_indexFirstFreeSlotRTV = 0; 45 | } 46 | else { 47 | m_indexFirstFreeSlotRTV = -1; 48 | } 49 | if (m_numDSVs) { 50 | descHeap.NumDescriptors = m_numDSVs; 51 | descHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; 52 | m_pDev->CreateDescriptorHeap(&descHeap, m_pheapDSV); 53 | m_pheapDSV->SetName(L"DSV Heap"); 54 | m_indexFirstFreeSlotDSV = 0; 55 | } 56 | else { 57 | m_indexFirstFreeSlotDSV = -1; 58 | } 59 | 60 | descHeap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; 61 | if (m_numCBVSRVUAVs) { 62 | descHeap.NumDescriptors = m_numCBVSRVUAVs; 63 | descHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; 64 | m_pDev->CreateDescriptorHeap(&descHeap, m_pheapCBVSRVUAV); 65 | m_pheapCBVSRVUAV->SetName(L"CBV/SRV/UAV Heap"); 66 | m_indexFirstFreeSlotCBVSRVUAV = 0; 67 | } 68 | else { 69 | m_indexFirstFreeSlotCBVSRVUAV = -1; 70 | } 71 | if (m_numSamplers) { 72 | descHeap.NumDescriptors = m_numSamplers; 73 | descHeap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; 74 | m_pDev->CreateDescriptorHeap(&descHeap, m_pheapSampler); 75 | m_pheapSampler->SetName(L"Sampler Heap"); 76 | m_indexFirstFreeSlotSampler = 0; 77 | } 78 | else { 79 | m_indexFirstFreeSlotSampler = -1; 80 | } 81 | 82 | m_sizeRTVHeapDesc = m_pDev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); 83 | m_sizeDSVHeapDesc = m_pDev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); 84 | m_sizeCBVSRVUAVHeapDesc = m_pDev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); 85 | m_sizeSamplerHeapDesc = m_pDev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); 86 | 87 | // Create an upload buffer. 88 | m_pDev->CreateCommittedResource(m_pUpload, &CD3DX12_RESOURCE_DESC::Buffer(DEFAULT_UPLOAD_BUFFER_SIZE), &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), 89 | D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr); 90 | m_iUpload = 0; 91 | } 92 | 93 | ResourceManager::~ResourceManager() { 94 | if (m_pUpload) { 95 | WaitForGPU(); 96 | 97 | m_pUpload->Release(); 98 | m_pUpload = nullptr; 99 | } 100 | 101 | m_pDev = nullptr; 102 | 103 | CloseHandle(m_hdlFenceEvent); 104 | 105 | if (m_pFence) { 106 | m_pFence->Release(); 107 | m_pFence = nullptr; 108 | } 109 | 110 | if (m_pCmdAllocator) { 111 | m_pCmdAllocator->Release(); 112 | m_pCmdAllocator = nullptr; 113 | } 114 | 115 | if (m_pCmdList) { 116 | m_pCmdList->Release(); 117 | m_pCmdList = nullptr; 118 | } 119 | 120 | while (!m_listFileData.empty()) { 121 | unsigned char* tmp = m_listFileData.back(); 122 | 123 | if (tmp) delete[] tmp; 124 | 125 | m_listFileData.pop_back(); 126 | } 127 | 128 | while (!m_listResources.empty()) { 129 | ID3D12Resource* tex = m_listResources.back(); 130 | 131 | if (tex) tex->Release(); 132 | 133 | m_listResources.pop_back(); 134 | } 135 | 136 | if (m_pheapSampler) { 137 | m_pheapSampler->Release(); 138 | m_pheapSampler = nullptr; 139 | } 140 | 141 | if (m_pheapCBVSRVUAV) { 142 | m_pheapCBVSRVUAV->Release(); 143 | m_pheapCBVSRVUAV = nullptr; 144 | } 145 | 146 | if (m_pheapDSV) { 147 | m_pheapDSV->Release(); 148 | m_pheapDSV = nullptr; 149 | } 150 | 151 | if (m_pheapRTV) { 152 | m_pheapRTV->Release(); 153 | m_pheapRTV = nullptr; 154 | } 155 | } 156 | 157 | void ResourceManager::AddRTV(ID3D12Resource* tex, D3D12_RENDER_TARGET_VIEW_DESC* desc, 158 | D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU) { 159 | if (m_indexFirstFreeSlotRTV >= m_numRTVs || m_indexFirstFreeSlotRTV < 0) { 160 | throw GFX_Exception("Error adding to RTV Heap. No space remaining."); 161 | } 162 | 163 | handleCPU = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_pheapRTV->GetCPUDescriptorHandleForHeapStart(), 164 | m_indexFirstFreeSlotRTV, m_sizeRTVHeapDesc); 165 | m_pDev->CreateRTV(tex, desc, handleCPU); 166 | ++m_indexFirstFreeSlotRTV; 167 | } 168 | 169 | void ResourceManager::AddDSV(ID3D12Resource* tex, D3D12_DEPTH_STENCIL_VIEW_DESC* desc, 170 | D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU) { 171 | if (m_indexFirstFreeSlotDSV >= m_numDSVs || m_indexFirstFreeSlotDSV < 0) { 172 | throw GFX_Exception("Error adding to DSV Heap. No space remaining."); 173 | } 174 | 175 | handleCPU = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_pheapDSV->GetCPUDescriptorHandleForHeapStart(), 176 | m_indexFirstFreeSlotDSV, m_sizeDSVHeapDesc); 177 | m_pDev->CreateDSV(tex, desc, handleCPU); 178 | ++m_indexFirstFreeSlotDSV; 179 | } 180 | 181 | void ResourceManager::AddCBV(D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU, 182 | D3D12_GPU_DESCRIPTOR_HANDLE& handleGPU) { 183 | if (m_indexFirstFreeSlotCBVSRVUAV >= m_numCBVSRVUAVs || m_indexFirstFreeSlotCBVSRVUAV < 0) { 184 | throw GFX_Exception("Error adding CBV to CBV/SRV/UAV Heap. No space remaining."); 185 | } 186 | 187 | handleCPU = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_pheapCBVSRVUAV->GetCPUDescriptorHandleForHeapStart(), 188 | m_indexFirstFreeSlotCBVSRVUAV, m_sizeCBVSRVUAVHeapDesc); 189 | handleGPU = CD3DX12_GPU_DESCRIPTOR_HANDLE(m_pheapCBVSRVUAV->GetGPUDescriptorHandleForHeapStart(), 190 | m_indexFirstFreeSlotCBVSRVUAV, m_sizeCBVSRVUAVHeapDesc); 191 | m_pDev->CreateCBV(desc, handleCPU); 192 | ++m_indexFirstFreeSlotCBVSRVUAV; 193 | } 194 | 195 | void ResourceManager::AddSRV(ID3D12Resource* tex, D3D12_SHADER_RESOURCE_VIEW_DESC* desc, 196 | D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU, D3D12_GPU_DESCRIPTOR_HANDLE& handleGPU) { 197 | if (m_indexFirstFreeSlotCBVSRVUAV >= m_numCBVSRVUAVs || m_indexFirstFreeSlotCBVSRVUAV < 0) { 198 | throw GFX_Exception("Error adding SRV to CBV/SRV/UAV Heap. No space remaining."); 199 | } 200 | 201 | handleCPU = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_pheapCBVSRVUAV->GetCPUDescriptorHandleForHeapStart(), 202 | m_indexFirstFreeSlotCBVSRVUAV, m_sizeCBVSRVUAVHeapDesc); 203 | handleGPU = CD3DX12_GPU_DESCRIPTOR_HANDLE(m_pheapCBVSRVUAV->GetGPUDescriptorHandleForHeapStart(), 204 | m_indexFirstFreeSlotCBVSRVUAV, m_sizeCBVSRVUAVHeapDesc); 205 | m_pDev->CreateSRV(tex, desc, handleCPU); 206 | ++m_indexFirstFreeSlotCBVSRVUAV; 207 | } 208 | 209 | void ResourceManager::AddSampler(D3D12_SAMPLER_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU) { 210 | if (m_indexFirstFreeSlotSampler >= m_numSamplers || m_indexFirstFreeSlotSampler < 0) { 211 | throw GFX_Exception("Error adding Sampler Heap. No space remaining."); 212 | } 213 | 214 | handleCPU = CD3DX12_CPU_DESCRIPTOR_HANDLE(m_pheapSampler->GetCPUDescriptorHandleForHeapStart(), m_indexFirstFreeSlotSampler, m_sizeSamplerHeapDesc); 215 | m_pDev->CreateSampler(desc, handleCPU); 216 | ++m_indexFirstFreeSlotSampler; 217 | } 218 | 219 | // takes a pointer to the existing resource, adds it to the list of resources, and returns the index to that resource. 220 | unsigned int ResourceManager::AddExistingResource(ID3D12Resource* tex) { 221 | m_listResources.push_back(tex); 222 | return (unsigned int)m_listResources.size() - 1; 223 | } 224 | 225 | // Create a new empty buffer. A pointer to the buffer is stored in buffer and index in list of resources is returned. 226 | unsigned int ResourceManager::NewBuffer(ID3D12Resource*& buffer, D3D12_RESOURCE_DESC* descBuffer, 227 | D3D12_HEAP_PROPERTIES* props, D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear) { 228 | m_pDev->CreateCommittedResource(buffer, descBuffer, props, flags, state, clear); 229 | 230 | m_listResources.push_back(buffer); 231 | return (unsigned int)m_listResources.size() - 1; 232 | } 233 | 234 | // Create a new empty buffer at index i in resource list, over-writing existing pointer. 235 | // A pointer to the buffer is stored in buffer and index in list of resources is returned. 236 | unsigned int ResourceManager::NewBufferAt(unsigned int i, ID3D12Resource*& buffer, D3D12_RESOURCE_DESC* descBuffer, 237 | D3D12_HEAP_PROPERTIES* props, D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear) { 238 | if (i < 0 || i >= m_listResources.size()) { 239 | std::string msg = "ResourceManager::NewBufferAt failed due to index " + std::to_string(i) + " out of bounds."; 240 | throw GFX_Exception(msg.c_str()); 241 | } 242 | m_pDev->CreateCommittedResource(buffer, descBuffer, props, flags, state, clear); 243 | 244 | m_listResources[i] = buffer; 245 | 246 | return i; 247 | } 248 | 249 | // Upload to the buffer stored at index i. 250 | void ResourceManager::UploadToBuffer(unsigned int i, unsigned int numSubResources, D3D12_SUBRESOURCE_DATA* data, D3D12_RESOURCE_STATES initialState) { 251 | if (i < 0 || i >= m_listResources.size()) { 252 | std::string msg = "ResourceManager::UploadToBuffer failed due to index " + std::to_string(i) + " out of bounds."; 253 | throw GFX_Exception(msg.c_str()); 254 | } 255 | 256 | if (FAILED(m_pCmdList->Reset(m_pCmdAllocator, NULL))) { 257 | throw GFX_Exception("ResourceManager::UploadToBuffer: CommandList Reset failed."); 258 | } 259 | 260 | m_pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_listResources[i], 261 | initialState, D3D12_RESOURCE_STATE_COPY_DEST)); 262 | 263 | auto size = GetRequiredIntermediateSize(m_listResources[i], 0, numSubResources); 264 | size = (UINT64)pow(2.0, ceil(log(size) / log(2))); // round size up to the next power of 2 to ensure good alignment in upload buffer. 265 | 266 | if (size > DEFAULT_UPLOAD_BUFFER_SIZE) { 267 | // then we're going to have to create a new temporary buffer. 268 | ID3D12Resource* tmpUpload; 269 | m_pDev->CreateCommittedResource(tmpUpload, &CD3DX12_RESOURCE_DESC::Buffer(size), &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), 270 | D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr); 271 | 272 | UpdateSubresources(m_pCmdList, m_listResources[i], tmpUpload, 0, 0, numSubResources, data); 273 | 274 | // set resource barriers to inform GPU that data is ready for use. 275 | m_pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_listResources[i], 276 | initialState, D3D12_RESOURCE_STATE_COPY_DEST)); 277 | 278 | // close the command list. 279 | if (FAILED(m_pCmdList->Close())) { 280 | throw GFX_Exception("ResourceManager::UploadToBuffer: CommandList Close failed."); 281 | } 282 | 283 | // load the command list. 284 | ID3D12CommandList* lCmds[] = { m_pCmdList }; 285 | m_pDev->ExecuteCommandLists(lCmds, __crt_countof(lCmds)); 286 | 287 | // add fence signal. 288 | ++m_valFence; 289 | m_pDev->SetFence(m_pFence, m_valFence); 290 | 291 | WaitForGPU(); 292 | tmpUpload->Release(); 293 | } else { 294 | if (size > DEFAULT_UPLOAD_BUFFER_SIZE - m_iUpload) { 295 | // then we need to wait for the GPU to finish with whatever it is currently uploading. 296 | // check to see if it is already done. 297 | if (m_pFence->GetCompletedValue() < m_valFence) { 298 | // then we're not done, so wait. 299 | WaitForGPU(); 300 | 301 | } 302 | m_iUpload = 0; 303 | } 304 | 305 | UpdateSubresources(m_pCmdList, m_listResources[i], m_pUpload, m_iUpload, 0, numSubResources, data); 306 | m_iUpload += size; 307 | 308 | // set resource barriers to inform GPU that data is ready for use. 309 | m_pCmdList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_listResources[i], 310 | D3D12_RESOURCE_STATE_COPY_DEST, initialState)); 311 | 312 | // close the command list. 313 | if (FAILED(m_pCmdList->Close())) { 314 | throw GFX_Exception("ResourceManager::UploadToBuffer: CommandList Close failed."); 315 | } 316 | 317 | // load the command list. 318 | ID3D12CommandList* lCmds[] = { m_pCmdList }; 319 | m_pDev->ExecuteCommandLists(lCmds, __crt_countof(lCmds)); 320 | 321 | // add fence signal. 322 | ++m_valFence; 323 | m_pDev->SetFence(m_pFence, m_valFence); 324 | } 325 | } 326 | 327 | // Wait for GPU to signal it has completed with the previous iteration of this frame. 328 | void ResourceManager::WaitForGPU() { 329 | if (FAILED(m_pFence->SetEventOnCompletion(m_valFence, m_hdlFenceEvent))) { 330 | throw GFX_Exception("ResourceManager::WaitForGPU failed to SetEventOnCompletion."); 331 | } 332 | 333 | WaitForSingleObject(m_hdlFenceEvent, INFINITE); 334 | } 335 | 336 | // return a pointer to the resource at the provided index 337 | ID3D12Resource* ResourceManager::GetResource(unsigned int index) { 338 | if (index < 0 || index >= m_listResources.size()) { 339 | std::string msg = "ResourceManager::GetResource failed due to index " + std::to_string(index) + " out of bounds."; 340 | throw GFX_Exception(msg.c_str()); 341 | } 342 | 343 | return m_listResources[index]; 344 | } 345 | 346 | // load a file and return the index of the data loaded in m_listFileData. 347 | unsigned int ResourceManager::LoadFile(const char* fn, unsigned int& h, unsigned int& w) { 348 | unsigned char* data; 349 | // load the black and white heightmap png file. Data is RGBA unsigned char. 350 | unsigned error = lodepng_decode32_file(&data, &w, &h, fn); 351 | if (error) { 352 | std::string msg = "ResourceManager::LoadFile: Error loading file " + std::string(fn); 353 | throw GFX_Exception(msg.c_str()); 354 | } 355 | 356 | m_listFileData.push_back(data); 357 | return (unsigned int)m_listFileData.size() - 1; 358 | } 359 | 360 | // get the data saved at index i in m_listFileData. 361 | unsigned char* ResourceManager::GetFileData(unsigned int i) { 362 | if (i < 0 || i >= m_listFileData.size()) { 363 | std::string msg = "ResourceManager::GetFileData: index out of bounds: " + std::to_string(i); 364 | throw GFX_Exception(msg.c_str()); 365 | } 366 | 367 | return m_listFileData[i]; 368 | } 369 | 370 | // tell the ResourceManager that you are done with the data saved at index i in m_listFileData. 371 | // it will delete that data. Leaves a NULL pointer in the list so as not to mess with other indices. 372 | void ResourceManager::UnloadFileData(unsigned int i) { 373 | if (i < 0 || i >= m_listFileData.size()) { 374 | std::string msg = "ResourceManager::UnloadFileData: index out of bounds: " + std::to_string(i); 375 | throw GFX_Exception(msg.c_str()); 376 | } 377 | 378 | delete[] m_listFileData[i]; 379 | } -------------------------------------------------------------------------------- /Render Terrain/ResourceManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | ResourceManager.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating and managing a Direct3D 12 Resource Maanager. 8 | 9 | Usage: - Proper shutdown is handled by the destructor. 10 | - Handles loading file data (LoadFile(), GetFileData()) 11 | - Manages all resource heaps. 12 | - Manages all ID3D12Resources. 13 | 14 | Future Work: - Add and remove resources dynamically. 15 | - Add support for loading different file types. Currently only supports PNG. 16 | - Add support for reserved and placed resources. 17 | */ 18 | #pragma once 19 | 20 | #include "Graphics.h" 21 | #include 22 | 23 | using namespace graphics; 24 | 25 | static const unsigned long long DEFAULT_UPLOAD_BUFFER_SIZE = 100000000; 26 | 27 | class ResourceManager { 28 | public: 29 | ResourceManager(Device* d, unsigned int numRTVs, unsigned int numDSVs, unsigned int numCBVSRVUAVs, 30 | unsigned int numSamplers); 31 | ~ResourceManager(); 32 | 33 | ID3D12DescriptorHeap* GetCBVSRVUAVHeap() { return m_pheapCBVSRVUAV; } 34 | 35 | void AddRTV(ID3D12Resource* tex, D3D12_RENDER_TARGET_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU); 36 | void AddDSV(ID3D12Resource* tex, D3D12_DEPTH_STENCIL_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU); 37 | void AddCBV(D3D12_CONSTANT_BUFFER_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU, 38 | D3D12_GPU_DESCRIPTOR_HANDLE& handleGPU); 39 | void AddSRV(ID3D12Resource* tex, D3D12_SHADER_RESOURCE_VIEW_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU, 40 | D3D12_GPU_DESCRIPTOR_HANDLE& handleGPU); 41 | void AddSampler(D3D12_SAMPLER_DESC* desc, D3D12_CPU_DESCRIPTOR_HANDLE& handleCPU); 42 | 43 | // takes a pointer to the existing resource, adds it to the list of resources, and returns the index to that resource. 44 | unsigned int AddExistingResource(ID3D12Resource* tex); 45 | // Create a new empty buffer. A pointer to the buffer is stored in buffer and index in list of resources is returned. 46 | unsigned int NewBuffer(ID3D12Resource*& buffer, D3D12_RESOURCE_DESC* descBuffer, D3D12_HEAP_PROPERTIES* props, 47 | D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear); 48 | // Create a new empty buffer at index i in resource list, over-writing existing pointer. 49 | // A pointer to the buffer is stored in buffer and index in list of resources is returned. 50 | unsigned int NewBufferAt(unsigned int i, ID3D12Resource*& buffer, D3D12_RESOURCE_DESC* descBuffer, 51 | D3D12_HEAP_PROPERTIES* props, D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state, D3D12_CLEAR_VALUE* clear); 52 | // Upload to the buffer stored at index i. 53 | void UploadToBuffer(unsigned int i, unsigned int numSubResources, D3D12_SUBRESOURCE_DATA* data, D3D12_RESOURCE_STATES initialState); 54 | 55 | // return a pointer to the resource at the provided index 56 | ID3D12Resource* GetResource(unsigned int index); 57 | 58 | // load a file and return the index of the data loaded in m_listFileData. 59 | unsigned int LoadFile(const char* fn, unsigned int& h, unsigned int& w); 60 | // get the data saved at index i in m_listFileData. 61 | unsigned char* GetFileData(unsigned int i); 62 | // tell the ResourceManager that you are done with the data saved at index i in m_listFileData. 63 | // it will delete that data. Leaves a NULL pointer in the list so as not to mess with other indices. 64 | void UnloadFileData(unsigned int i); 65 | // Wait for GPU to signal it has completed with the previous iteration of this frame. 66 | void WaitForGPU(); 67 | 68 | private: 69 | Device* m_pDev; 70 | ID3D12CommandAllocator* m_pCmdAllocator; 71 | ID3D12GraphicsCommandList* m_pCmdList; 72 | ID3D12Fence* m_pFence; 73 | HANDLE m_hdlFenceEvent; 74 | ID3D12DescriptorHeap* m_pheapRTV; // Render Target View Heap. 75 | ID3D12DescriptorHeap* m_pheapDSV; // Depth Stencil View Heap. 76 | ID3D12DescriptorHeap* m_pheapCBVSRVUAV; // Constant Buffer View, Shader Resource View, and Unordered Access View heap. 77 | ID3D12DescriptorHeap* m_pheapSampler; // Sampler heap. 78 | std::vector m_listResources; 79 | std::vector m_listFileData; // Any data loaded from files. 80 | ID3D12Resource* m_pUpload; 81 | unsigned long long m_iUpload; // index into upload buffer where free space starts. 82 | unsigned long long m_valFence; // Value to check fence against to confirm GPU is done. 83 | unsigned int m_numRTVs; 84 | unsigned int m_numDSVs; 85 | unsigned int m_numCBVSRVUAVs; 86 | unsigned int m_numSamplers; 87 | unsigned int m_indexFirstFreeSlotRTV; // index into RTV heap of the first unused slot. 88 | unsigned int m_indexFirstFreeSlotDSV; 89 | unsigned int m_indexFirstFreeSlotCBVSRVUAV; 90 | unsigned int m_indexFirstFreeSlotSampler; 91 | unsigned int m_sizeRTVHeapDesc; // Heap descriptor size for render target view heaps. 92 | unsigned int m_sizeDSVHeapDesc; // Heap descriptor size for depth stencil view heaps. 93 | unsigned int m_sizeCBVSRVUAVHeapDesc; // Heap descriptor size for CBV, SRV, and UAV heaps. 94 | unsigned int m_sizeSamplerHeapDesc; // Heap descriptor size for Sampler Heaps. 95 | }; 96 | -------------------------------------------------------------------------------- /Render Terrain/Scene.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Scene.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for creating, managing, and rendering a scene. 8 | */ 9 | #include "Scene.h" 10 | #include 11 | 12 | Scene::Scene(int height, int width, Device* DEV) : 13 | m_ResMgr(DEV, FRAME_BUFFER_COUNT, 6, 23, 0), m_Cam(height, width), m_DNC(6000, 4096) { 14 | m_pDev = DEV; 15 | m_pT = nullptr; 16 | 17 | for (int i = 0; i < FRAME_BUFFER_COUNT; ++i) { 18 | m_pFrames[i] = new Frame(i, m_pDev, &m_ResMgr, height, width, 4096); 19 | } 20 | 21 | XMFLOAT4 colors[] = { XMFLOAT4(0.35f, 0.5f, 0.18f, 1.0f), XMFLOAT4(0.89f, 0.89f, 0.89f, 1.0f), 22 | XMFLOAT4(0.31f, 0.25f, 0.2f, 1.0f), XMFLOAT4(0.39f, 0.37f, 0.38f, 1.0f) }; 23 | m_pT = new Terrain(&m_ResMgr, new TerrainMaterial(&m_ResMgr, "grassnormals.png", "snownormals.png", 24 | "dirtnormals.png", "rocknormals.png", "grassdiffuse.png", "snowdiffuse.png", "dirtdiffuse.png", 25 | "rockdiffuse.png", colors), "heightmap6.png", "displacement.png"); 26 | 27 | m_ResMgr.WaitForGPU(); 28 | 29 | m_pDev->CreateGraphicsCommandList(D3D12_COMMAND_LIST_TYPE_DIRECT, m_pFrames[0]->GetAllocator(), m_pCmdList); 30 | CloseCommandLists(); 31 | 32 | // create a viewport and scissor rectangle. 33 | m_vpMain.TopLeftX = 0; 34 | m_vpMain.TopLeftY = 0; 35 | m_vpMain.Width = (float)width; 36 | m_vpMain.Height = (float)height; 37 | m_vpMain.MinDepth = 0; 38 | m_vpMain.MaxDepth = 1; 39 | 40 | m_srMain.left = 0; 41 | m_srMain.top = 0; 42 | m_srMain.right = width; 43 | m_srMain.bottom = height; 44 | 45 | // Initialize Graphics Pipelines for 2D and 3D rendering. 46 | InitPipelineTerrain2D(); 47 | InitPipelineTerrain3D(); 48 | InitPipelineShadowMap(); 49 | } 50 | 51 | Scene::~Scene() { 52 | while (!m_listRootSigs.empty()) { 53 | ID3D12RootSignature* sigRoot = m_listRootSigs.back(); 54 | 55 | if (sigRoot) sigRoot->Release(); 56 | 57 | m_listRootSigs.pop_back(); 58 | } 59 | 60 | while (!m_listPSOs.empty()) { 61 | ID3D12PipelineState* pso = m_listPSOs.back(); 62 | 63 | if (pso) pso->Release(); 64 | 65 | m_listPSOs.pop_back(); 66 | } 67 | 68 | if (m_pT) { 69 | delete m_pT; 70 | } 71 | 72 | for (int i = 0; i < FRAME_BUFFER_COUNT; ++i) { 73 | delete m_pFrames[i]; 74 | } 75 | 76 | m_pDev = nullptr; 77 | } 78 | 79 | // Close all command lists. Currently there is only the one. 80 | void Scene::CloseCommandLists() { 81 | // close the command list. 82 | if (FAILED(m_pCmdList->Close())) { 83 | throw GFX_Exception("Scene::CloseCommandLists failed."); 84 | } 85 | } 86 | 87 | // Initialize the root signature and pipeline state object for rendering the terrain in 2D. 88 | void Scene::InitPipelineTerrain2D() { 89 | // set up the Root Signature. 90 | // create a descriptor table with 1 entry for the descriptor heap containing our SRV to the heightmap. 91 | CD3DX12_DESCRIPTOR_RANGE rangesRoot[3]; 92 | CD3DX12_ROOT_PARAMETER paramsRoot[3]; 93 | // height map 94 | rangesRoot[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); 95 | paramsRoot[0].InitAsDescriptorTable(1, &rangesRoot[0]); 96 | // displacement map 97 | rangesRoot[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); 98 | paramsRoot[1].InitAsDescriptorTable(1, &rangesRoot[1]); 99 | // terrain constants 100 | rangesRoot[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0); 101 | paramsRoot[2].InitAsDescriptorTable(1, &rangesRoot[2]); 102 | 103 | // create our texture sampler for the heightmap. 104 | CD3DX12_STATIC_SAMPLER_DESC descSamplers[1]; 105 | descSamplers[0].Init(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 106 | 107 | // It isn't really necessary to deny the other shaders access, but it does technically allow the GPU to optimize more. 108 | CD3DX12_ROOT_SIGNATURE_DESC descRoot; 109 | descRoot.Init(_countof(paramsRoot), paramsRoot, 1, descSamplers, D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | 110 | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | 111 | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | 112 | D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS); 113 | ID3D12RootSignature* sigRoot; 114 | m_pDev->CreateRootSig(&descRoot, sigRoot); 115 | m_listRootSigs.push_back(sigRoot); // save a copy of the pointer to the root signature. 116 | 117 | D3D12_SHADER_BYTECODE bcPS = {}; 118 | D3D12_SHADER_BYTECODE bcVS = {}; 119 | CompileShader(L"RenderTerrain2dVS.hlsl", VERTEX_SHADER, bcVS); 120 | CompileShader(L"RenderTerrain2dPS.hlsl", PIXEL_SHADER, bcPS); 121 | 122 | DXGI_SAMPLE_DESC sampleDesc = {}; 123 | sampleDesc.Count = 1; // turns multi-sampling off. Not supported feature for my card. 124 | 125 | // create the pipeline state object 126 | D3D12_GRAPHICS_PIPELINE_STATE_DESC descPSO = {}; 127 | descPSO.pRootSignature = sigRoot; 128 | descPSO.VS = bcVS; 129 | descPSO.PS = bcPS; 130 | descPSO.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; 131 | descPSO.NumRenderTargets = 1; 132 | descPSO.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; 133 | descPSO.SampleDesc = sampleDesc; 134 | descPSO.SampleMask = UINT_MAX; 135 | descPSO.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); 136 | descPSO.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; 137 | descPSO.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); 138 | descPSO.DepthStencilState.DepthEnable = false; 139 | descPSO.DepthStencilState.StencilEnable = false; 140 | 141 | ID3D12PipelineState* pso; 142 | m_pDev->CreatePSO(&descPSO, pso); 143 | m_listPSOs.push_back(pso); // save a copy of the pointer to the PSO. 144 | } 145 | 146 | // Initialize the root signature and pipeline state object for rendering the terrain in 3D. 147 | void Scene::InitPipelineTerrain3D() { 148 | // set up the Root Signature. 149 | // create a descriptor table. 150 | CD3DX12_ROOT_PARAMETER paramsRoot[6]; 151 | CD3DX12_DESCRIPTOR_RANGE rangesRoot[6]; 152 | 153 | // height map 154 | rangesRoot[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); 155 | paramsRoot[0].InitAsDescriptorTable(1, &rangesRoot[0]); 156 | // displacement map 157 | rangesRoot[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); 158 | paramsRoot[1].InitAsDescriptorTable(1, &rangesRoot[1]); 159 | // terrain constants 160 | rangesRoot[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0); 161 | paramsRoot[2].InitAsDescriptorTable(1, &rangesRoot[2]); 162 | // frame constants 163 | rangesRoot[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1); 164 | paramsRoot[3].InitAsDescriptorTable(1, &rangesRoot[3]); 165 | // shadow atlas 166 | rangesRoot[4].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 2); 167 | paramsRoot[4].InitAsDescriptorTable(1, &rangesRoot[4]); 168 | // material 169 | rangesRoot[5].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 3); 170 | paramsRoot[5].InitAsDescriptorTable(1, &rangesRoot[5], D3D12_SHADER_VISIBILITY_PIXEL); 171 | 172 | // create our texture samplers for the heightmap. 173 | CD3DX12_STATIC_SAMPLER_DESC descSamplers[4]; 174 | descSamplers[0].Init(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 175 | descSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; 176 | descSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 177 | descSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 178 | descSamplers[1].Init(1, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 179 | descSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_DOMAIN; 180 | descSamplers[2].Init(2, D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT); 181 | descSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; 182 | descSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; 183 | descSamplers[2].MaxAnisotropy = 1; 184 | descSamplers[2].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; 185 | descSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; 186 | descSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; 187 | descSamplers[3].Init(3, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 188 | descSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; 189 | 190 | // It isn't really necessary to deny the other shaders access, but it does technically allow the GPU to optimize more. 191 | CD3DX12_ROOT_SIGNATURE_DESC descRoot; 192 | descRoot.Init(_countof(paramsRoot), paramsRoot, 4, descSamplers, D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | 193 | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); 194 | ID3D12RootSignature* sigRoot; 195 | m_pDev->CreateRootSig(&descRoot, sigRoot); 196 | m_listRootSigs.push_back(sigRoot); 197 | 198 | D3D12_SHADER_BYTECODE bcPS = {}; 199 | D3D12_SHADER_BYTECODE bcVS = {}; 200 | D3D12_SHADER_BYTECODE bcHS = {}; 201 | D3D12_SHADER_BYTECODE bcDS = {}; 202 | CompileShader(L"RenderTerrainTessVS.hlsl", VERTEX_SHADER, bcVS); 203 | CompileShader(L"RenderTerrainTessPS.hlsl", PIXEL_SHADER, bcPS); 204 | CompileShader(L"RenderTerrainTessHS.hlsl", HULL_SHADER, bcHS); 205 | CompileShader(L"RenderTerrainTessDS.hlsl", DOMAIN_SHADER, bcDS); 206 | 207 | DXGI_SAMPLE_DESC descSample = {}; 208 | descSample.Count = 1; // turns multi-sampling off. Not supported feature for my card. 209 | 210 | // create the pipeline state object 211 | // create input layout. 212 | D3D12_INPUT_LAYOUT_DESC descInputLayout = {}; 213 | D3D12_INPUT_ELEMENT_DESC descElementLayout[] = { 214 | { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 215 | { "POSITION", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 216 | { "POSITION", 2, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 217 | { "SKIRT", 0, DXGI_FORMAT_R32_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } 218 | }; 219 | 220 | descInputLayout.NumElements = sizeof(descElementLayout) / sizeof(D3D12_INPUT_ELEMENT_DESC); 221 | descInputLayout.pInputElementDescs = descElementLayout; 222 | 223 | D3D12_GRAPHICS_PIPELINE_STATE_DESC descPSO = {}; 224 | descPSO.pRootSignature = sigRoot; 225 | descPSO.InputLayout = descInputLayout; 226 | descPSO.VS = bcVS; 227 | descPSO.PS = bcPS; 228 | descPSO.HS = bcHS; 229 | descPSO.DS = bcDS; 230 | descPSO.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; 231 | descPSO.NumRenderTargets = 1; 232 | descPSO.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; 233 | descPSO.DSVFormat = DXGI_FORMAT_D32_FLOAT; 234 | descPSO.SampleDesc = descSample; 235 | descPSO.SampleMask = UINT_MAX; 236 | descPSO.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); 237 | descPSO.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; 238 | descPSO.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; 239 | descPSO.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); 240 | descPSO.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT); 241 | 242 | ID3D12PipelineState* pso; 243 | m_pDev->CreatePSO(&descPSO, pso); 244 | m_listPSOs.push_back(pso); 245 | } 246 | 247 | // Initialize the root signature and pipeline state object for rendering to the shadow map. 248 | void Scene::InitPipelineShadowMap() { 249 | // set up the Root Signature. 250 | // create a descriptor table with 2 entries for the descriptor heap containing our SRV to the heightmap and our CBV. 251 | CD3DX12_ROOT_PARAMETER paramsRoot[4]; 252 | CD3DX12_DESCRIPTOR_RANGE rangesRoot[4]; 253 | 254 | // heightmap 255 | rangesRoot[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); 256 | paramsRoot[0].InitAsDescriptorTable(1, &rangesRoot[0]); 257 | // displacement map 258 | rangesRoot[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1); 259 | paramsRoot[1].InitAsDescriptorTable(1, &rangesRoot[1]); 260 | // terrain constants 261 | rangesRoot[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0); 262 | paramsRoot[2].InitAsDescriptorTable(1, &rangesRoot[2]); 263 | // shadow constants 264 | rangesRoot[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 1); 265 | paramsRoot[3].InitAsDescriptorTable(1, &rangesRoot[3]); 266 | 267 | // create our texture samplers for the heightmap. 268 | CD3DX12_STATIC_SAMPLER_DESC descSamplers[2]; 269 | descSamplers[0].Init(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 270 | descSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_DOMAIN; 271 | descSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 272 | descSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; 273 | descSamplers[1].Init(1, D3D12_FILTER_MIN_MAG_MIP_LINEAR); 274 | descSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_DOMAIN; 275 | 276 | // It isn't really necessary to deny the other shaders access, but it does technically allow the GPU to optimize more. 277 | CD3DX12_ROOT_SIGNATURE_DESC descRoot; 278 | descRoot.Init(_countof(paramsRoot), paramsRoot, 2, descSamplers, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS); 279 | ID3D12RootSignature* sigRoot; 280 | m_pDev->CreateRootSig(&descRoot, sigRoot); 281 | m_listRootSigs.push_back(sigRoot); 282 | 283 | D3D12_SHADER_BYTECODE bcVS = {}; 284 | D3D12_SHADER_BYTECODE bcHS = {}; 285 | D3D12_SHADER_BYTECODE bcDS = {}; 286 | 287 | CompileShader(L"RenderTerrainTessVS.hlsl", VERTEX_SHADER, bcVS); 288 | CompileShader(L"RenderShadowMapHS.hlsl", HULL_SHADER, bcHS); 289 | CompileShader(L"RenderShadowMapDS.hlsl", DOMAIN_SHADER, bcDS); 290 | 291 | DXGI_SAMPLE_DESC descSample = {}; 292 | descSample.Count = 1; // turns multi-sampling off. Not supported feature for my card. 293 | 294 | // create the pipeline state object 295 | // create input layout. 296 | D3D12_INPUT_LAYOUT_DESC descInputLayout = {}; 297 | D3D12_INPUT_ELEMENT_DESC descElementLayout[] = { 298 | { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 299 | { "POSITION", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 300 | { "POSITION", 2, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, 301 | { "SKIRT", 0, DXGI_FORMAT_R32_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 } 302 | }; 303 | 304 | descInputLayout.NumElements = sizeof(descElementLayout) / sizeof(D3D12_INPUT_ELEMENT_DESC); 305 | descInputLayout.pInputElementDescs = descElementLayout; 306 | 307 | D3D12_GRAPHICS_PIPELINE_STATE_DESC descPSO = {}; 308 | descPSO.pRootSignature = sigRoot; 309 | descPSO.InputLayout = descInputLayout; 310 | descPSO.VS = bcVS; 311 | descPSO.HS = bcHS; 312 | descPSO.DS = bcDS; 313 | descPSO.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; 314 | descPSO.RTVFormats[0] = DXGI_FORMAT_UNKNOWN; 315 | descPSO.NumRenderTargets = 0; 316 | descPSO.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; 317 | descPSO.SampleDesc = descSample; 318 | descPSO.SampleMask = UINT_MAX; 319 | descPSO.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); 320 | descPSO.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; 321 | descPSO.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; 322 | descPSO.RasterizerState.DepthBias = 10000; 323 | descPSO.RasterizerState.DepthBiasClamp = 0.0f; 324 | descPSO.RasterizerState.SlopeScaledDepthBias = 1.0f; 325 | descPSO.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); 326 | descPSO.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT); 327 | 328 | ID3D12PipelineState* pso; 329 | m_pDev->CreatePSO(&descPSO, pso); 330 | m_listPSOs.push_back(pso); 331 | } 332 | 333 | void Scene::SetViewport(ID3D12GraphicsCommandList* cmdList) { 334 | cmdList->RSSetViewports(1, &m_vpMain); 335 | cmdList->RSSetScissorRects(1, &m_srMain); 336 | } 337 | 338 | void Scene::DrawShadowMap(ID3D12GraphicsCommandList* cmdList) { 339 | m_pFrames[m_iFrame]->BeginShadowPass(cmdList); 340 | 341 | cmdList->SetPipelineState(m_listPSOs[2]); 342 | cmdList->SetGraphicsRootSignature(m_listRootSigs[2]); 343 | 344 | ID3D12DescriptorHeap* heaps[] = { m_ResMgr.GetCBVSRVUAVHeap() }; 345 | cmdList->SetDescriptorHeaps(_countof(heaps), heaps); 346 | 347 | // Tell the terrain to attach its resources. 348 | m_pT->AttachTerrainResources(cmdList, 0, 1, 2); 349 | 350 | for (int i = 0; i < 4; ++i) { 351 | // fill in this cascade's shadow constants. 352 | ShadowMapShaderConstants constants; 353 | constants.shadowViewProj = m_DNC.GetShadowViewProjMatrix(i); 354 | constants.eye = m_Cam.GetEyePosition(); 355 | m_DNC.GetShadowFrustum(i, constants.frustum); 356 | m_pFrames[m_iFrame]->SetShadowConstants(constants, i); 357 | m_pFrames[m_iFrame]->AttachShadowPassResources(i, cmdList, 3); 358 | 359 | // mDrawMode = 0/false for 2D rendering and 1/true for 3D rendering 360 | m_pT->Draw(cmdList, true); 361 | } 362 | 363 | m_pFrames[m_iFrame]->EndShadowPass(cmdList); 364 | } 365 | 366 | void Scene::DrawTerrain(ID3D12GraphicsCommandList* cmdList) { 367 | const float clearColor[] = { 0.2f, 0.6f, 1.0f, 1.0f }; 368 | m_pFrames[m_iFrame]->BeginRenderPass(cmdList, clearColor); 369 | 370 | cmdList->SetPipelineState(m_listPSOs[m_drawMode]); 371 | cmdList->SetGraphicsRootSignature(m_listRootSigs[m_drawMode]); 372 | 373 | SetViewport(cmdList); 374 | 375 | ID3D12DescriptorHeap* heaps[] = { m_ResMgr.GetCBVSRVUAVHeap() }; 376 | cmdList->SetDescriptorHeaps(_countof(heaps), heaps); 377 | 378 | // Tell the terrain to attach its resources. 379 | m_pT->AttachTerrainResources(cmdList, 0, 1, 2); 380 | 381 | if (m_drawMode) { 382 | // set the constant buffers. 383 | XMFLOAT4 frustum[6]; 384 | m_Cam.GetViewFrustum(frustum); 385 | 386 | PerFrameConstantBuffer constants; 387 | constants.viewproj = m_Cam.GetViewProjectionMatrixTransposed(); 388 | for (int i = 0; i < 4; ++i) { 389 | constants.shadowtexmatrices[i] = m_DNC.GetShadowViewProjTexMatrix(i); 390 | } 391 | constants.eye = m_Cam.GetEyePosition(); 392 | constants.frustum[0] = frustum[0]; 393 | constants.frustum[1] = frustum[1]; 394 | constants.frustum[2] = frustum[2]; 395 | constants.frustum[3] = frustum[3]; 396 | constants.frustum[4] = frustum[4]; 397 | constants.frustum[5] = frustum[5]; 398 | constants.light = m_DNC.GetLight(); 399 | constants.useTextures = m_UseTextures; 400 | m_pFrames[m_iFrame]->SetFrameConstants(constants); 401 | m_pFrames[m_iFrame]->AttachFrameResources(cmdList, 4, 3); 402 | 403 | m_pT->AttachMaterialResources(cmdList, 5); 404 | } 405 | 406 | // mDrawMode = 0/false for 2D rendering and 1/true for 3D rendering 407 | m_pT->Draw(cmdList, (bool)m_drawMode); 408 | 409 | m_pFrames[m_iFrame]->EndRenderPass(cmdList); 410 | } 411 | 412 | void Scene::Draw() { 413 | m_pFrames[m_iFrame]->Reset(); 414 | m_pFrames[m_iFrame]->AttachCommandList(m_pCmdList); 415 | 416 | DrawShadowMap(m_pCmdList); 417 | 418 | DrawTerrain(m_pCmdList); 419 | 420 | CloseCommandLists(); 421 | ID3D12CommandList* lCmds[] = { m_pCmdList }; 422 | m_pDev->ExecuteCommandLists(lCmds, __crt_countof(lCmds)); 423 | m_pDev->Present(); 424 | } 425 | 426 | void Scene::Update() { 427 | if (m_LockToTerrain) { 428 | XMFLOAT4 eye = m_Cam.GetEyePosition(); 429 | float h = m_pT->GetHeightAtPoint(eye.x, eye.y) + 2; 430 | m_Cam.LockPosition(XMFLOAT4(eye.x, eye.y, h, 1.0f)); 431 | } 432 | 433 | m_DNC.Update(m_pT->GetBoundingSphere(), &m_Cam); 434 | 435 | m_iFrame = m_pDev->GetCurrentBackBuffer(); 436 | Draw(); 437 | } 438 | 439 | // function allowing the main program to pass keyboard input to the scene. 440 | void Scene::HandleKeyboardInput(UINT key) { 441 | switch (key) { 442 | case _W: 443 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(MOVE_STEP, 0.0f, 0.0f)); 444 | break; 445 | case _S: 446 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(-MOVE_STEP, 0.0f, 0.0f)); 447 | break; 448 | case _A: 449 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(0.0f, MOVE_STEP, 0.0f)); 450 | break; 451 | case _D: 452 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(0.0f, -MOVE_STEP, 0.0f)); 453 | break; 454 | case _Q: 455 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(0.0f, 0.0f, MOVE_STEP)); 456 | break; 457 | case _Z: 458 | if (m_drawMode > 0) m_Cam.Translate(XMFLOAT3(0.0f, 0.0f, -MOVE_STEP)); 459 | break; 460 | case _1: // draw in 2D. draw heightmap. 461 | m_drawMode = 0; 462 | break; 463 | case _2: // draw in 3D. 464 | m_drawMode = 1; 465 | break; 466 | case _T: 467 | m_UseTextures = !m_UseTextures; 468 | break; 469 | case _L: 470 | m_LockToTerrain = !m_LockToTerrain; 471 | break; 472 | case VK_SPACE: 473 | m_DNC.TogglePause(); 474 | break; 475 | } 476 | } 477 | 478 | // function allowing the main program to pass mouse input to the scene. 479 | void Scene::HandleMouseInput(int x, int y) { 480 | if (m_drawMode > 0) { 481 | m_Cam.Pitch(ROT_ANGLE * y); 482 | m_Cam.Yaw(-ROT_ANGLE * x); 483 | } 484 | } -------------------------------------------------------------------------------- /Render Terrain/Scene.h: -------------------------------------------------------------------------------- 1 | /* 2 | Scene.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating, managing, and rendering a scene. 8 | 9 | Usage: - Proper shutdown is handled by the destructor. 10 | - Requires a pointer to a Device object be passed in. 11 | - Is hard-coded for Direct3D 12. 12 | - Call Update() in the main loop to render the scene. 13 | - Press T to toggle between textured or coloured. 14 | - Press 1 for 2D view. 15 | - Press 2 for 3D view. 16 | 17 | Future Work: - Add support for multi-threaded rendering. 18 | - Add support for mip-maps. 19 | - Add sky box. 20 | - Add atmospheric scattering. 21 | - Add dynamic terrain mesh, ie geometry clipmapping. 22 | - Add support for loading multiple terrains. 23 | - Add support for other objects. 24 | - Add support to lock camera to terrain. 25 | */ 26 | #pragma once 27 | 28 | #include "Frame.h" 29 | #include "ResourceManager.h" 30 | #include "Terrain.h" 31 | #include "Camera.h" 32 | #include "DayNightCycle.h" 33 | 34 | using namespace graphics; 35 | 36 | enum InputKeys { _0 = 0x30, _1, _2, _3, _4, _5, _6, _7, _8, _9, _A = 0x41, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z }; 37 | #define MOVE_STEP 1.0f 38 | #define ROT_ANGLE 0.75f 39 | 40 | static const int FRAME_BUFFER_COUNT = 3; // triple buffering. 41 | 42 | class Scene { 43 | public: 44 | Scene(int height, int width, Device* DEV); 45 | ~Scene(); 46 | 47 | void Update(); 48 | void Draw(); 49 | // function allowing the main program to pass keyboard input to the scene. 50 | void HandleKeyboardInput(UINT key); 51 | // function allowing the main program to pass mouse input to the scene. 52 | void HandleMouseInput(int x, int y); 53 | 54 | private: 55 | // Close all command lists. Currently there is only the one. 56 | void CloseCommandLists(); 57 | // Set the viewport and scissor rectangle for the scene. 58 | void SetViewport(ID3D12GraphicsCommandList* cmdList); 59 | 60 | // Initialize the root signature and pipeline state object for rendering the terrain in 2D. 61 | void InitPipelineTerrain2D(); 62 | // Initialize the root signature and pipeline state object for rendering the terrain in 3D. 63 | void InitPipelineTerrain3D(); 64 | // Initialize the root signature and pipeline state object for rendering to the shadow map. 65 | void InitPipelineShadowMap(); 66 | // Draw the terrain in both 3D and 2D 67 | void DrawTerrain(ID3D12GraphicsCommandList* cmdList); 68 | // Render the shadow map 69 | void DrawShadowMap(ID3D12GraphicsCommandList* cmdList); 70 | 71 | Device* m_pDev; 72 | ResourceManager m_ResMgr; 73 | Frame* m_pFrames[::FRAME_BUFFER_COUNT]; 74 | ID3D12GraphicsCommandList* m_pCmdList; 75 | Terrain* m_pT; 76 | Camera m_Cam; 77 | DayNightCycle m_DNC; 78 | D3D12_VIEWPORT m_vpMain; 79 | D3D12_RECT m_srMain; 80 | int m_drawMode = 0; 81 | std::vector m_listRootSigs; 82 | std::vector m_listPSOs; 83 | int m_iFrame = 0; 84 | bool m_UseTextures = false; 85 | bool m_LockToTerrain = true; 86 | }; 87 | 88 | -------------------------------------------------------------------------------- /Render Terrain/Snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/Snow.png -------------------------------------------------------------------------------- /Render Terrain/Terrain.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Terrain.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for loading a heightmap and rendering as a terrain. 8 | */ 9 | #include "lodepng.h" 10 | #include "Terrain.h" 11 | #include "Common.h" 12 | 13 | Terrain::Terrain(ResourceManager* rm, TerrainMaterial* mat, const char* fnHeightmap, const char* fnDisplacementMap) : 14 | m_pMat(mat), m_pResMgr(rm) { 15 | m_dataHeightMap = nullptr; 16 | m_dataDisplacementMap = nullptr; 17 | m_dataVertices = nullptr; 18 | m_dataIndices = nullptr; 19 | m_pConstants = nullptr; 20 | 21 | LoadHeightMap(fnHeightmap); 22 | LoadDisplacementMap(fnDisplacementMap); 23 | 24 | CreateMesh3D(); 25 | } 26 | 27 | Terrain::~Terrain() { 28 | // The order resources are released appears to matter. I haven't tested all possible orders, but at least releasing the heap 29 | // and resources after the pso and rootsig was causing my GPU to hang on shutdown. Using the current order resolved that issue. 30 | m_dataHeightMap = nullptr; 31 | m_dataDisplacementMap = nullptr; 32 | 33 | DeleteVertexAndIndexArrays(); 34 | 35 | m_pResMgr = nullptr; 36 | delete m_pMat; 37 | } 38 | 39 | void Terrain::Draw(ID3D12GraphicsCommandList* cmdList, bool Draw3D) { 40 | if (Draw3D) { 41 | cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST); // describe how to read the vertex buffer. 42 | cmdList->IASetVertexBuffers(0, 1, &m_viewVertexBuffer); 43 | cmdList->IASetIndexBuffer(&m_viewIndexBuffer); 44 | 45 | cmdList->DrawIndexedInstanced(m_numIndices, 1, 0, 0, 0); 46 | } else { 47 | // draw in 2D 48 | cmdList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // describe how to read the vertex buffer. 49 | cmdList->DrawInstanced(3, 1, 0, 0); 50 | } 51 | } 52 | 53 | // Clean up array data. 54 | void Terrain::DeleteVertexAndIndexArrays() { 55 | if (m_dataVertices) { 56 | delete[] m_dataVertices; 57 | m_dataVertices = nullptr; 58 | } 59 | 60 | if (m_dataIndices) { 61 | delete[] m_dataIndices; 62 | m_dataIndices = nullptr; 63 | } 64 | 65 | if (m_pConstants) { 66 | delete m_pConstants; 67 | m_pConstants = nullptr; 68 | } 69 | } 70 | 71 | // generate vertex and index buffers for 3D mesh of terrain 72 | void Terrain::CreateMesh3D() { 73 | // Create a vertex buffer 74 | m_scaleHeightMap = (float)m_wHeightMap / 16.0f; 75 | int tessFactor = 8; 76 | int scalePatchX = m_wHeightMap / tessFactor; 77 | int scalePatchY = m_hHeightMap / tessFactor; 78 | int numVertsInTerrain = scalePatchX * scalePatchY; 79 | // number of vertices needed for terrain + base vertices for skirt. 80 | m_numVertices = numVertsInTerrain + scalePatchX * 4; 81 | 82 | // create a vertex array 1/4 the size of the height map in each dimension, 83 | // to be stretched over the height map 84 | int arrSize = (int)(m_numVertices); 85 | m_dataVertices = new Vertex[arrSize]; 86 | for (int y = 0; y < scalePatchY; ++y) { 87 | for (int x = 0; x < scalePatchX; ++x) { 88 | m_dataVertices[y * scalePatchX + x].position = XMFLOAT3((float)x * tessFactor, (float)y * tessFactor, ((float)m_dataHeightMap[((y * m_wHeightMap + x) * 4) * tessFactor] / 255.0f) * m_scaleHeightMap); 89 | m_dataVertices[y * scalePatchX + x].skirt = 5; 90 | } 91 | } 92 | 93 | XMFLOAT2 zBounds = CalcZBounds(m_dataVertices[0], m_dataVertices[numVertsInTerrain - 1]); 94 | m_hBase = zBounds.x - 10; 95 | 96 | // create base vertices for side 1 of skirt. y = 0. 97 | int iVertex = numVertsInTerrain; 98 | for (int x = 0; x < scalePatchX; ++x) { 99 | m_dataVertices[iVertex].position = XMFLOAT3((float)(x * tessFactor), 0.0f, m_hBase); 100 | m_dataVertices[iVertex++].skirt = 1; 101 | } 102 | 103 | // create base vertices for side 2 of skirt. y = m_hHeightMap - tessFactor. 104 | for (int x = 0; x < scalePatchX; ++x) { 105 | m_dataVertices[iVertex].position = XMFLOAT3((float)(x * tessFactor), (float)(m_hHeightMap - tessFactor), m_hBase); 106 | m_dataVertices[iVertex++].skirt = 2; 107 | } 108 | 109 | // create base vertices for side 3 of skirt. x = 0. 110 | for (int y = 0; y < scalePatchY; ++y) { 111 | m_dataVertices[iVertex].position = XMFLOAT3(0.0f, (float)(y * tessFactor), m_hBase); 112 | m_dataVertices[iVertex++].skirt = 3; 113 | } 114 | 115 | // create base vertices for side 4 of skirt. x = m_wHeightMap - tessFactor. 116 | for (int y = 0; y < scalePatchY; ++y) { 117 | m_dataVertices[iVertex].position = XMFLOAT3((float)(m_wHeightMap - tessFactor), (float)(y * tessFactor), m_hBase); 118 | m_dataVertices[iVertex++].skirt = 4; 119 | } 120 | 121 | // create an index buffer 122 | // our grid is scalePatchX * scalePatchY in size. 123 | // the vertices are oriented like so: 124 | // 0, 1, 2, 3, 4, 125 | // 5, 6, 7, 8, 9, 126 | // 10, 11, 12, 13, 14 127 | // need to add indices for 4 edge skirts (2 x-aligned, 2 y-aligned, 4 indices per patch). 128 | // + 4 indices for patch representing bottom plane. 129 | arrSize = (scalePatchX - 1) * (scalePatchY - 1) * 4 + 2 * 4 * (scalePatchX - 1) + 2 * 4 * (scalePatchY - 1) + 4; 130 | m_dataIndices = new UINT[arrSize]; 131 | int i = 0; 132 | for (int y = 0; y < scalePatchY - 1; ++y) { 133 | for (int x = 0; x < scalePatchX - 1; ++x) { 134 | UINT vert0 = x + y * scalePatchX; 135 | UINT vert1 = x + 1 + y * scalePatchX; 136 | UINT vert2 = x + (y + 1) * scalePatchX; 137 | UINT vert3 = x + 1 + (y + 1) * scalePatchX; 138 | m_dataIndices[i++] = vert0; 139 | m_dataIndices[i++] = vert1; 140 | m_dataIndices[i++] = vert2; 141 | m_dataIndices[i++] = vert3; 142 | 143 | // now that we have the indices for our patch, we need to calculate the bounding box. 144 | // z bounds is a bit harder as we need to find the max and min y values in the heightmap for the patch range. 145 | // store it in the first vertex 146 | // subtract one from coords of min and add one to coords of max to take into account 147 | // the offsets caused by displacement, which should always be between -1 and 1. 148 | XMFLOAT2 bz = CalcZBounds(m_dataVertices[vert0], m_dataVertices[vert3]); 149 | m_dataVertices[vert0].aabbmin = XMFLOAT3(m_dataVertices[vert0].position.x - 0.5f, m_dataVertices[vert0].position.y - 0.5f, bz.x - 0.5f); 150 | m_dataVertices[vert0].aabbmax = XMFLOAT3(m_dataVertices[vert3].position.x + 0.5f, m_dataVertices[vert3].position.y + 0.5f, bz.y + 0.5f); 151 | } 152 | } 153 | 154 | // so as not to interfere with the terrain wrt bounds for frustum culling, we need the 0th control point of each skirt patch to be a base vertex as defined above. 155 | // add indices for side 1 of skirt. y = 0. 156 | iVertex = numVertsInTerrain; 157 | for (int x = 0; x < scalePatchX - 1; ++x) { 158 | m_dataIndices[i++] = iVertex; // control point 0 159 | m_dataIndices[i++] = iVertex + 1; // control point 1 160 | m_dataIndices[i++] = x; // control point 2 161 | m_dataIndices[i++] = x + 1; // control point 3 162 | XMFLOAT2 bz = CalcZBounds(m_dataVertices[x], m_dataVertices[x + 1]); 163 | m_dataVertices[iVertex].aabbmin = XMFLOAT3((float)(x * tessFactor), 0.0f, m_hBase); 164 | m_dataVertices[iVertex++].aabbmax = XMFLOAT3((float)((x + 1) * tessFactor), 0.0f, bz.y); 165 | } 166 | // add indices for side 2 of skirt. y = m_hHeightMap - tessFactor. 167 | ++iVertex; 168 | for (int x = 0; x < scalePatchX - 1; ++x) { 169 | m_dataIndices[i++] = iVertex + 1; 170 | m_dataIndices[i++] = iVertex; 171 | int offset = scalePatchX * (scalePatchY - 1); 172 | m_dataIndices[i++] = x + offset + 1; 173 | m_dataIndices[i++] = x + offset; 174 | XMFLOAT2 bz = CalcZBounds(m_dataVertices[x + offset], m_dataVertices[x + offset + 1]); 175 | m_dataVertices[++iVertex].aabbmin = XMFLOAT3((float)(x * tessFactor), (float)(m_hHeightMap - tessFactor), m_hBase); 176 | m_dataVertices[iVertex].aabbmax = XMFLOAT3((float)((x + 1) * tessFactor), (float)(m_hHeightMap - tessFactor), bz.y); 177 | } 178 | // add indices for side 3 of skirt. x = 0. 179 | ++iVertex; 180 | for (int y = 0; y < scalePatchY - 1; ++y) { 181 | m_dataIndices[i++] = iVertex + 1; 182 | m_dataIndices[i++] = iVertex; 183 | m_dataIndices[i++] = (y + 1) * scalePatchX; 184 | m_dataIndices[i++] = y * scalePatchX; 185 | XMFLOAT2 bz = CalcZBounds(m_dataVertices[y * scalePatchX], m_dataVertices[(y + 1) * scalePatchX]); 186 | m_dataVertices[++iVertex].aabbmin = XMFLOAT3(0.0f, (float)(y * tessFactor), m_hBase); 187 | m_dataVertices[iVertex].aabbmax = XMFLOAT3(0.0f, (float)((y + 1) * tessFactor), bz.y); 188 | } 189 | // add indices for side 4 of skirt. x = m_wHeightMap - tessFactor. 190 | ++iVertex; 191 | for (int y = 0; y < scalePatchY - 1; ++y) { 192 | m_dataIndices[i++] = iVertex; 193 | m_dataIndices[i++] = iVertex + 1; 194 | m_dataIndices[i++] = y * scalePatchX + scalePatchX - 1; 195 | m_dataIndices[i++] = (y + 1) * scalePatchX + scalePatchX - 1; 196 | XMFLOAT2 bz = CalcZBounds(m_dataVertices[y * scalePatchX + scalePatchX - 1], m_dataVertices[(y + 1) * scalePatchX + scalePatchX - 1]); 197 | m_dataVertices[iVertex].aabbmin = XMFLOAT3((float)(m_wHeightMap - tessFactor), (float)(y * tessFactor), m_hBase); 198 | m_dataVertices[iVertex++].aabbmax = XMFLOAT3((float)(m_wHeightMap - tessFactor), (float)((y + 1) * tessFactor), bz.y); 199 | } 200 | // add indices for bottom plane. 201 | m_dataIndices[i++] = numVertsInTerrain + scalePatchX - 1; 202 | m_dataIndices[i++] = numVertsInTerrain; 203 | m_dataIndices[i++] = numVertsInTerrain + scalePatchX + scalePatchX - 1; 204 | m_dataIndices[i++] = numVertsInTerrain + scalePatchX; 205 | m_dataVertices[numVertsInTerrain + scalePatchX - 1].aabbmin = XMFLOAT3(0.0f, 0.0f, m_hBase); 206 | m_dataVertices[numVertsInTerrain + scalePatchX - 1].aabbmax = XMFLOAT3((float)m_wHeightMap, (float)m_hHeightMap, m_hBase); 207 | m_dataVertices[numVertsInTerrain + scalePatchX - 1].skirt = 0; 208 | 209 | m_numIndices = arrSize; 210 | 211 | CreateVertexBuffer(); 212 | CreateIndexBuffer(); 213 | CreateConstantBuffer(); 214 | 215 | // Create a bounding sphere for the height map. 216 | float w = (float)m_wHeightMap / 2.0f; 217 | float h = (float)m_hHeightMap / 2.0f; 218 | m_BoundingSphere.SetCenter(w, h, (zBounds.y + zBounds.x) / 2.0f); 219 | m_BoundingSphere.SetRadius(sqrtf(w * w + h * h)); 220 | } 221 | 222 | // Create the vertex buffer view 223 | void Terrain::CreateVertexBuffer() { 224 | // Create the vertex buffer 225 | ID3D12Resource* buffer; 226 | auto iBuffer = m_pResMgr->NewBuffer(buffer, &CD3DX12_RESOURCE_DESC::Buffer(m_numVertices * sizeof(Vertex)), 227 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 228 | D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, nullptr); 229 | buffer->SetName(L"Terrain Vertex Buffer"); 230 | auto sizeofVertexBuffer = GetRequiredIntermediateSize(buffer, 0, 1); 231 | 232 | // prepare vertex data for upload. 233 | D3D12_SUBRESOURCE_DATA dataVB = {}; 234 | dataVB.pData = m_dataVertices; 235 | dataVB.RowPitch = sizeofVertexBuffer; 236 | dataVB.SlicePitch = sizeofVertexBuffer; 237 | 238 | m_pResMgr->UploadToBuffer(iBuffer, 1, &dataVB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); 239 | 240 | // create and save vertex buffer view to Terrain object. 241 | m_viewVertexBuffer = {}; 242 | m_viewVertexBuffer.BufferLocation = buffer->GetGPUVirtualAddress(); 243 | m_viewVertexBuffer.StrideInBytes = sizeof(Vertex); 244 | m_viewVertexBuffer.SizeInBytes = (UINT)sizeofVertexBuffer; 245 | } 246 | 247 | // Create the index buffer view 248 | void Terrain::CreateIndexBuffer() { 249 | // Create the index buffer 250 | ID3D12Resource* buffer; 251 | auto iBuffer = m_pResMgr->NewBuffer(buffer, &CD3DX12_RESOURCE_DESC::Buffer(m_numIndices * sizeof(UINT)), 252 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 253 | D3D12_RESOURCE_STATE_INDEX_BUFFER, nullptr); 254 | buffer->SetName(L"Terrain Index Buffer"); 255 | auto sizeofIndexBuffer = GetRequiredIntermediateSize(buffer, 0, 1); 256 | 257 | // prepare index data for upload. 258 | D3D12_SUBRESOURCE_DATA dataIB = {}; 259 | dataIB.pData = m_dataIndices; 260 | dataIB.RowPitch = sizeofIndexBuffer; 261 | dataIB.SlicePitch = sizeofIndexBuffer; 262 | 263 | m_pResMgr->UploadToBuffer(iBuffer, 1, &dataIB, D3D12_RESOURCE_STATE_INDEX_BUFFER); 264 | 265 | // create and save index buffer view to Terrain object. 266 | m_viewIndexBuffer = {}; 267 | m_viewIndexBuffer.BufferLocation = buffer->GetGPUVirtualAddress(); 268 | m_viewIndexBuffer.Format = DXGI_FORMAT_R32_UINT; 269 | m_viewIndexBuffer.SizeInBytes = (UINT)sizeofIndexBuffer; 270 | } 271 | 272 | // Create the constant buffer for terrain shader constants 273 | void Terrain::CreateConstantBuffer() { 274 | // Create the constant buffer 275 | ID3D12Resource* buffer; 276 | auto iBuffer = m_pResMgr->NewBuffer(buffer, &CD3DX12_RESOURCE_DESC::Buffer(sizeof(TerrainShaderConstants)), 277 | &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 278 | D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, nullptr); 279 | buffer->SetName(L"Terrain Shader Constants Buffer"); 280 | auto sizeofBuffer = GetRequiredIntermediateSize(buffer, 0, 1); 281 | 282 | // prepare constant buffer data for upload. 283 | m_pConstants = new TerrainShaderConstants(m_scaleHeightMap, (float)m_wHeightMap, (float)m_hHeightMap, m_hBase); 284 | D3D12_SUBRESOURCE_DATA dataCB = {}; 285 | dataCB.pData = m_pConstants; 286 | dataCB.RowPitch = sizeofBuffer; 287 | dataCB.SlicePitch = sizeofBuffer; 288 | 289 | m_pResMgr->UploadToBuffer(iBuffer, 1, &dataCB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); 290 | 291 | // Create a constant buffer view. 292 | D3D12_CONSTANT_BUFFER_VIEW_DESC descCBV = {}; 293 | descCBV.BufferLocation = buffer->GetGPUVirtualAddress(); 294 | descCBV.SizeInBytes = (sizeof(TerrainShaderConstants) + 255) & ~255; // CB size is required to be 256-byte aligned. 295 | 296 | m_pResMgr->AddCBV(&descCBV, m_hdlConstantsCBV_CPU, m_hdlConstantsCBV_GPU); 297 | } 298 | 299 | // calculate the minimum and maximum z values for vertices between the provided bounds. 300 | XMFLOAT2 Terrain::CalcZBounds(Vertex bottomLeft, Vertex topRight) { 301 | float max = -100000; 302 | float min = 100000; 303 | int bottomLeftX = bottomLeft.position.x == 0 ? (int)bottomLeft.position.x: (int)bottomLeft.position.x - 1; 304 | int bottomLeftY = bottomLeft.position.y == 0 ? (int)bottomLeft.position.y : (int)bottomLeft.position.y - 1; 305 | int topRightX = topRight.position.x >= m_wHeightMap ? (int)topRight.position.x : (int)topRight.position.x + 1; 306 | int topRightY = topRight.position.y >= m_wHeightMap ? (int)topRight.position.y : (int)topRight.position.y + 1; 307 | 308 | for (int y = bottomLeftY; y <= topRightY; ++y) { 309 | for (int x = bottomLeftX; x <= topRightX; ++x) { 310 | float z = ((float)m_dataHeightMap[(x + y * m_wHeightMap) * 4] / 255.0f) * m_scaleHeightMap; 311 | 312 | if (z > max) max = z; 313 | if (z < min) min = z; 314 | } 315 | } 316 | 317 | return XMFLOAT2(min, max); 318 | } 319 | 320 | // load the specified file containing the heightmap data. 321 | void Terrain::LoadHeightMap(const char* fnHeightMap) { 322 | unsigned int index; 323 | index = m_pResMgr->LoadFile(fnHeightMap, m_hHeightMap, m_wHeightMap); 324 | m_dataHeightMap = m_pResMgr->GetFileData(index); 325 | 326 | // Create the texture buffers. 327 | D3D12_RESOURCE_DESC descTex = {}; 328 | descTex.MipLevels = 1; 329 | descTex.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 330 | descTex.Width = m_wHeightMap; 331 | descTex.Height = m_hHeightMap; 332 | descTex.Flags = D3D12_RESOURCE_FLAG_NONE; 333 | descTex.DepthOrArraySize = 1; 334 | descTex.SampleDesc.Count = 1; 335 | descTex.SampleDesc.Quality = 0; 336 | descTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 337 | 338 | ID3D12Resource* hm; 339 | unsigned int iBuffer = m_pResMgr->NewBuffer(hm, &descTex, &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 340 | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr); 341 | hm->SetName(L"Height Map"); 342 | 343 | // prepare height map data for upload. 344 | D3D12_SUBRESOURCE_DATA dataTex = {}; 345 | dataTex.pData = m_dataHeightMap; 346 | dataTex.RowPitch = m_wHeightMap * 4 * sizeof(unsigned char); 347 | dataTex.SlicePitch = m_hHeightMap * m_wHeightMap * 4 * sizeof(unsigned char); 348 | 349 | m_pResMgr->UploadToBuffer(iBuffer, 1, &dataTex, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 350 | 351 | // Create the SRV for the detail map texture and save to Terrain object. 352 | D3D12_SHADER_RESOURCE_VIEW_DESC descSRV = {}; 353 | descSRV.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 354 | descSRV.Format = descTex.Format; 355 | descSRV.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 356 | descSRV.Texture2D.MipLevels = 1; 357 | 358 | m_pResMgr->AddSRV(hm, &descSRV, m_hdlHeightMapSRV_CPU, m_hdlHeightMapSRV_GPU); 359 | } 360 | 361 | void Terrain::LoadDisplacementMap(const char* fnMap) { 362 | unsigned int index; 363 | index = m_pResMgr->LoadFile(fnMap, m_hDisplacementMap, m_wDisplacementMap); 364 | m_dataDisplacementMap = m_pResMgr->GetFileData(index); 365 | 366 | // Create the texture buffers. 367 | D3D12_RESOURCE_DESC descTex = {}; 368 | descTex.MipLevels = 1; 369 | descTex.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 370 | descTex.Width = m_wDisplacementMap; 371 | descTex.Height = m_hDisplacementMap; 372 | descTex.Flags = D3D12_RESOURCE_FLAG_NONE; 373 | descTex.DepthOrArraySize = 1; 374 | descTex.SampleDesc.Count = 1; 375 | descTex.SampleDesc.Quality = 0; 376 | descTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; 377 | 378 | ID3D12Resource* dm; 379 | unsigned int iBuffer = m_pResMgr->NewBuffer(dm, &descTex, &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, 380 | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, nullptr); 381 | dm->SetName(L"Displacement Map"); 382 | 383 | D3D12_SUBRESOURCE_DATA dataTex = {}; 384 | // prepare height map data for upload. 385 | dataTex.pData = m_dataDisplacementMap; 386 | dataTex.RowPitch = m_wDisplacementMap * 4 * sizeof(unsigned char); 387 | dataTex.SlicePitch = m_hDisplacementMap * m_wDisplacementMap * 4 * sizeof(unsigned char); 388 | 389 | m_pResMgr->UploadToBuffer(iBuffer, 1, &dataTex, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); 390 | 391 | // Create the SRV for the detail map texture and save to Terrain object. 392 | D3D12_SHADER_RESOURCE_VIEW_DESC descSRV = {}; 393 | descSRV.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; 394 | descSRV.Format = descTex.Format; 395 | descSRV.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; 396 | descSRV.Texture2D.MipLevels = 1; 397 | 398 | m_pResMgr->AddSRV(dm, &descSRV, m_hdlDisplacementMapSRV_CPU, m_hdlDisplacementMapSRV_GPU); 399 | } 400 | 401 | // Attach the resources needed for rendering terrain. 402 | // Requires the indices into the root descriptor table to attach the heightmap and displacement map SRVs and constant buffer CBV to. 403 | void Terrain::AttachTerrainResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndexHeightMap, 404 | unsigned int srvDescTableIndexDisplacementMap, unsigned int cbvDescTableIndex) { 405 | cmdList->SetGraphicsRootDescriptorTable(srvDescTableIndexHeightMap, m_hdlHeightMapSRV_GPU); 406 | cmdList->SetGraphicsRootDescriptorTable(srvDescTableIndexDisplacementMap, m_hdlDisplacementMapSRV_GPU); 407 | cmdList->SetGraphicsRootDescriptorTable(cbvDescTableIndex, m_hdlConstantsCBV_GPU); 408 | } 409 | 410 | // Attach the material resources. Requires root descriptor table index. 411 | void Terrain::AttachMaterialResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex) { 412 | m_pMat->Attach(cmdList, srvDescTableIndex); 413 | } 414 | 415 | float Terrain::GetHeightMapValueAtPoint(float x, float y) { 416 | // use bilinear interpolation to calculate the height of the base terrain at point (x, y). 417 | float x1 = floorf(x); 418 | x1 = x1 < 0.0f ? 0.0f : x1 > m_wHeightMap ? m_wHeightMap : x1; 419 | float x2 = ceilf(x); 420 | x2 = x2 < 0.0f ? 0.0f : x2 > m_wHeightMap ? m_wHeightMap : x2; 421 | float dx = x - x1; 422 | float y1 = floorf(y); 423 | y1 = y1 < 0.0f ? 0.0f : y1 > m_hHeightMap ? m_hHeightMap : y1; 424 | float y2 = ceilf(y); 425 | y2 = y2 < 0.0f ? 0.0f : y2 > m_hHeightMap ? m_hHeightMap : y2; 426 | float dy = y - y1; 427 | 428 | float a = (float)m_dataHeightMap[(int)(y1 * m_wHeightMap + x1) * 4] / 255.0f; 429 | float b = (float)m_dataHeightMap[(int)(y1 * m_wHeightMap + x2) * 4] / 255.0f; 430 | float c = (float)m_dataHeightMap[(int)(y2 * m_wHeightMap + x1) * 4] / 255.0f; 431 | float d = (float)m_dataHeightMap[(int)(y2 * m_wHeightMap + x2) * 4] / 255.0f; 432 | 433 | return bilerp(a, b, c, d, dx, dy); 434 | } 435 | 436 | float Terrain::GetDisplacementMapValueAtPoint(float x, float y) { 437 | float _x = x / m_wDisplacementMap / 32; 438 | float _y = y / m_hDisplacementMap / 32; 439 | // use bilinear interpolation to calculate the height of the base terrain at point (x, y). 440 | float x1 = floorf(_x); 441 | float x2 = ceilf(_x); 442 | float dx = _x - x1; 443 | float y1 = floorf(_y); 444 | float y2 = ceilf(_y); 445 | float dy = _y - y1; 446 | 447 | float a = (float)m_dataDisplacementMap[(int)(y1 * m_wDisplacementMap + x1) * 4 + 3] / 255.0f; 448 | float b = (float)m_dataDisplacementMap[(int)(y1 * m_wDisplacementMap + x2) * 4 + 3] / 255.0f; 449 | float c = (float)m_dataDisplacementMap[(int)(y2 * m_wDisplacementMap + x1) * 4 + 3] / 255.0f; 450 | float d = (float)m_dataDisplacementMap[(int)(y2 * m_wDisplacementMap + x2) * 4 + 3] / 255.0f; 451 | 452 | return bilerp(a, b, c, d, dx, dy); 453 | } 454 | 455 | XMFLOAT3 Terrain::CalculateNormalAtPoint(float x, float y) { 456 | XMFLOAT2 b(x, y - 0.3f / m_hHeightMap); 457 | XMFLOAT2 c(x + 0.3f / m_wHeightMap, y - 0.3f / m_hHeightMap); 458 | XMFLOAT2 d(x + 0.3f / m_wHeightMap, y); 459 | XMFLOAT2 e(x + 0.3f / m_wHeightMap, y + 0.3f / m_hHeightMap); 460 | XMFLOAT2 f(x, y + 0.3f / m_hHeightMap); 461 | XMFLOAT2 g(x - 0.3f / m_wHeightMap, y + 0.3f / m_hHeightMap); 462 | XMFLOAT2 h(x - 0.3f / m_wHeightMap, y); 463 | XMFLOAT2 i(x - 0.3f / m_wHeightMap, y - 0.3f / m_hHeightMap); 464 | 465 | float zb = GetHeightMapValueAtPoint(b.x, b.y) * m_scaleHeightMap; 466 | float zc = GetHeightMapValueAtPoint(c.x, c.y) * m_scaleHeightMap; 467 | float zd = GetHeightMapValueAtPoint(d.x, d.y) * m_scaleHeightMap; 468 | float ze = GetHeightMapValueAtPoint(e.x, e.y) * m_scaleHeightMap; 469 | float zf = GetHeightMapValueAtPoint(f.x, f.y) * m_scaleHeightMap; 470 | float zg = GetHeightMapValueAtPoint(g.x, g.y) * m_scaleHeightMap; 471 | float zh = GetHeightMapValueAtPoint(h.x, h.y) * m_scaleHeightMap; 472 | float zi = GetHeightMapValueAtPoint(i.x, i.y) * m_scaleHeightMap; 473 | 474 | float u = zg + 2 * zh + zi - zc - 2 * zd - ze; 475 | float v = 2 * zb + zc + zi - ze - 2 * zf - zg; 476 | float w = 8.0f; 477 | 478 | XMFLOAT3 norm(u, v, w); 479 | XMVECTOR normalized = XMVector3Normalize(XMLoadFloat3(&norm)); 480 | XMStoreFloat3(&norm, normalized); 481 | 482 | return norm; 483 | } 484 | 485 | float Terrain::GetHeightAtPoint(float x, float y) { 486 | float z = GetHeightMapValueAtPoint(x, y) * m_scaleHeightMap; 487 | float d = 2.0f * GetDisplacementMapValueAtPoint(x, y) - 1.0f; 488 | XMFLOAT3 norm = CalculateNormalAtPoint(x, y); 489 | XMFLOAT3 pos(x, y, z); 490 | XMVECTOR normal = XMLoadFloat3(&norm); 491 | XMVECTOR position = XMLoadFloat3(&pos); 492 | XMVECTOR posFinal = position + normal * 0.5f * d; 493 | XMFLOAT3 fp; 494 | XMStoreFloat3(&fp, posFinal); 495 | 496 | return fp.z; 497 | } -------------------------------------------------------------------------------- /Render Terrain/Terrain.h: -------------------------------------------------------------------------------- 1 | /* 2 | Terrain.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 14, 2016 6 | 7 | Description: Class for loading height map and displacement map data 8 | with which to render terrain. Also contains a reference 9 | to a TerrainMaterial object containing diffuse and 10 | normal maps to texture the terrain with. 11 | 12 | Usage: - Proper shutdown is handled by the destructor. 13 | - Is hard-coded for Direct3D 12. 14 | - Call AttachTerrainResources() to load the appropriate 15 | buffer/resource views to render the terrain. 16 | - Call AttachMaterialResources() to load the material 17 | SRVs in order to texture the terrain. 18 | - Call Draw() and pass a Command List to load the set of 19 | commands necessary to render the terrain. 20 | 21 | Future Work: - Add a colour palette. 22 | - Add bounding sphere code. 23 | - Change to dynamic mesh, ie geometry clipmapping or similar. 24 | */ 25 | #pragma once 26 | 27 | #include "Graphics.h" 28 | #include "Material.h" 29 | #include "BoundingVolume.h" 30 | #include 31 | 32 | using namespace graphics; 33 | 34 | struct Vertex { 35 | XMFLOAT3 position; 36 | XMFLOAT3 aabbmin; 37 | XMFLOAT3 aabbmax; 38 | UINT skirt; 39 | }; 40 | 41 | struct TerrainShaderConstants { 42 | float scale; 43 | float width; 44 | float depth; 45 | float base; 46 | 47 | TerrainShaderConstants(float s, float w, float d, float b) : scale(s), width(w), depth(d), base(b) {} 48 | }; 49 | 50 | class Terrain { 51 | public: 52 | Terrain(ResourceManager* rm, TerrainMaterial* mat, const char* fnHeightmap, const char* fnDisplacementMap); 53 | ~Terrain(); 54 | 55 | void Draw(ID3D12GraphicsCommandList* cmdList, bool Draw3D = true); 56 | // Attach the resources needed for rendering terrain. 57 | // Requires the indices into the root descriptor table to attach the heightmap and displacement map SRVs and constant buffer CBV to. 58 | void AttachTerrainResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndexHeightMap, 59 | unsigned int srvDescTableIndexDisplacementMap, unsigned int cbvDescTableIndex); 60 | // Attach the material resources. Requires root descriptor table index. 61 | void AttachMaterialResources(ID3D12GraphicsCommandList* cmdList, unsigned int srvDescTableIndex); 62 | 63 | BoundingSphere GetBoundingSphere() { return m_BoundingSphere; } 64 | float GetHeightAtPoint(float x, float y); 65 | 66 | private: 67 | // Generates an array of vertices and an array of indices. 68 | void CreateMesh3D(); 69 | // Create the vertex buffer view 70 | void CreateVertexBuffer(); 71 | // Create the index buffer view 72 | void CreateIndexBuffer(); 73 | // Create the constant buffer for terrain shader constants 74 | void CreateConstantBuffer(); 75 | // load the specified file containing the heightmap data. 76 | void LoadHeightMap(const char* fnHeightMap); 77 | // load the specified file containing a displacement map used for smaller geometry detail. 78 | void LoadDisplacementMap(const char* fnMap); 79 | // calculate the minimum and maximum z values for vertices between the provide bounds. 80 | XMFLOAT2 CalcZBounds(Vertex topLeft, Vertex bottomRight); 81 | // Clean up array data 82 | void DeleteVertexAndIndexArrays(); 83 | 84 | float GetHeightMapValueAtPoint(float x, float y); 85 | float GetDisplacementMapValueAtPoint(float x, float y); 86 | XMFLOAT3 CalculateNormalAtPoint(float x, float y); 87 | 88 | TerrainMaterial* m_pMat; 89 | ResourceManager* m_pResMgr; 90 | D3D12_VERTEX_BUFFER_VIEW m_viewVertexBuffer; 91 | D3D12_INDEX_BUFFER_VIEW m_viewIndexBuffer; 92 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlHeightMapSRV_CPU; 93 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlHeightMapSRV_GPU; 94 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlDisplacementMapSRV_CPU; 95 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlDisplacementMapSRV_GPU; 96 | D3D12_CPU_DESCRIPTOR_HANDLE m_hdlConstantsCBV_CPU; 97 | D3D12_GPU_DESCRIPTOR_HANDLE m_hdlConstantsCBV_GPU; 98 | unsigned char* m_dataHeightMap; 99 | unsigned char* m_dataDisplacementMap; 100 | unsigned int m_wHeightMap; 101 | unsigned int m_hHeightMap; 102 | unsigned int m_wDisplacementMap; 103 | unsigned int m_hDisplacementMap; 104 | float m_hBase; 105 | unsigned long m_numVertices; 106 | unsigned long m_numIndices; 107 | float m_scaleHeightMap; 108 | Vertex* m_dataVertices; // buffer to contain vertex array prior to upload. 109 | UINT* m_dataIndices; // buffer to contain index array prior to upload. 110 | TerrainShaderConstants* m_pConstants; 111 | BoundingSphere m_BoundingSphere; 112 | }; 113 | 114 | -------------------------------------------------------------------------------- /Render Terrain/Window.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Window.cpp 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating a window in a Win32 application. 8 | */ 9 | #include "Window.h" 10 | 11 | using namespace window; 12 | 13 | Window::Window(LPCWSTR appName, int height, int width, WNDPROC WndProc, bool isFullscreen) { 14 | m_nameApp = appName; 15 | m_hWindow = height; 16 | m_wWindow = width; 17 | m_isFullscreen = isFullscreen; 18 | 19 | // Get the instance of this application. 20 | m_Instance = GetModuleHandle(NULL); 21 | 22 | // Setup the windows class with default settings. 23 | WNDCLASSEX wc; 24 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 25 | wc.lpfnWndProc = WndProc; 26 | wc.cbClsExtra = 0; 27 | wc.cbWndExtra = 0; 28 | wc.hInstance = m_Instance; 29 | wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 30 | wc.hIconSm = wc.hIcon; 31 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 32 | wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); 33 | wc.lpszMenuName = NULL; 34 | wc.lpszClassName = m_nameApp; 35 | wc.cbSize = sizeof(WNDCLASSEX); 36 | 37 | // Register the window class. 38 | if (RegisterClassEx(&wc) == 0) { 39 | throw Window_Exception("Failed to RegisterClassEx on window init."); 40 | } 41 | 42 | // Setup the screen settings depending on whether it is running in full screen or in windowed mode. 43 | DEVMODE dmScreenSettings; 44 | int posX, posY, h, w; 45 | 46 | // Determine the resolution of the clients desktop screen. 47 | h = GetSystemMetrics(SM_CYSCREEN); 48 | w = GetSystemMetrics(SM_CXSCREEN); 49 | 50 | if (m_isFullscreen) { 51 | // If full screen set, the screen to maximum size of the users desktop and 32bit. 52 | memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); 53 | dmScreenSettings.dmSize = sizeof(dmScreenSettings); 54 | dmScreenSettings.dmPelsHeight = (unsigned long)h; 55 | dmScreenSettings.dmPelsWidth = (unsigned long)w; 56 | dmScreenSettings.dmBitsPerPel = 32; 57 | dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 58 | 59 | // Change the display settings to full screen. 60 | if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { 61 | throw Window_Exception("ChangeDisplaySettings for fullscreen on window init failed."); 62 | } 63 | // Set the position of the window to the top left corner. 64 | posX = posY = 0; 65 | m_hWindow = h; 66 | m_wWindow = w; 67 | } 68 | else { 69 | // Place the window in the middle of the screen. 70 | posX = (w - m_wWindow) / 2; 71 | posY = (h - m_hWindow) / 2; 72 | // set the height and width to what we want the window to be. 73 | h = m_hWindow; 74 | w = m_wWindow; 75 | } 76 | 77 | // Create the window with the screen settings and get the handle to it. 78 | m_Window = CreateWindowEx(WS_EX_APPWINDOW, m_nameApp, m_nameApp, WS_OVERLAPPEDWINDOW, posX, posY, w, h, NULL, NULL, m_Instance, NULL); 79 | if (!m_Window) { 80 | throw Window_Exception("Failed to CreateWindowEx on window init."); 81 | } 82 | 83 | // Bring the window up on the screen and set it as main focus. 84 | ShowWindow(m_Window, SW_SHOW); // returns 0 if window was previously hidden, non-zero if it wasn't. We don't care so ignoring return value. 85 | SetForegroundWindow(m_Window); // returns zero if window already in foreground, non-zero if it wasn't. We don't care so ignoring return value. 86 | 87 | // Hide the mouse cursor. 88 | ShowCursor(false); // returns the current display count. We don't really care, so we're ignoring it, but it should be zero. 89 | } 90 | 91 | Window::~Window() { 92 | // Show the mouse cursor. 93 | ShowCursor(true); 94 | 95 | // Fix the display settings if leaving full screen mode. 96 | if (m_isFullscreen) { 97 | ChangeDisplaySettings(NULL, 0); 98 | } 99 | 100 | // Remove the window. 101 | DestroyWindow(m_Window); 102 | m_Window = NULL; 103 | 104 | // Remove the application instance. 105 | UnregisterClass(m_nameApp, m_Instance); 106 | m_Instance = NULL; 107 | } 108 | -------------------------------------------------------------------------------- /Render Terrain/Window.h: -------------------------------------------------------------------------------- 1 | /* 2 | Window.h 3 | 4 | Author: Chris Serson 5 | Last Edited: October 12, 2016 6 | 7 | Description: Class for creating a window in a Win32 application. 8 | 9 | Usage: Calling the constructor, either through Window WIN(...); 10 | or Window* WIN; WIN = new Window(...);, will initialize 11 | and create a window. 12 | Proper shutdown is handled by the destructor. 13 | Future Work: - Add ability to switch between windowed and full screen modes 14 | at run time. 15 | - Add resize window option. 16 | - Add ability to change resolution when in full screen. Currently 17 | Defaults to current monitor resolution. 18 | - Make some of the hard coded options more generic for future use. 19 | ie, ShowCursor(false); called in constructor should be optional and toggleable. 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | namespace window { 27 | class Window_Exception : public std::runtime_error { 28 | public: 29 | Window_Exception(const char *msg) : std::runtime_error(msg) {} 30 | }; 31 | 32 | class Window 33 | { 34 | public: 35 | Window(LPCWSTR appName, int height, int width, WNDPROC WndProc, bool isFullscreen); 36 | ~Window(); 37 | 38 | HWND GetWindow() { return m_Window; } 39 | int Height() { return m_hWindow; } 40 | int Width() { return m_wWindow; } 41 | 42 | private: 43 | HINSTANCE m_Instance; 44 | HWND m_Window; 45 | bool m_isFullscreen; 46 | int m_hWindow; 47 | int m_wWindow; 48 | LPCWSTR m_nameApp; 49 | }; 50 | } -------------------------------------------------------------------------------- /Render Terrain/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/dirt.png -------------------------------------------------------------------------------- /Render Terrain/dirtdepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/dirtdepthmap.png -------------------------------------------------------------------------------- /Render Terrain/dirtdiffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/dirtdiffuse.png -------------------------------------------------------------------------------- /Render Terrain/dirtnormalmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/dirtnormalmap.png -------------------------------------------------------------------------------- /Render Terrain/dirtnormals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/dirtnormals.png -------------------------------------------------------------------------------- /Render Terrain/displacement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/displacement.png -------------------------------------------------------------------------------- /Render Terrain/displacementmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/displacementmap.png -------------------------------------------------------------------------------- /Render Terrain/displacementmapnormals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/displacementmapnormals.png -------------------------------------------------------------------------------- /Render Terrain/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/grass.png -------------------------------------------------------------------------------- /Render Terrain/grassdepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/grassdepthmap.png -------------------------------------------------------------------------------- /Render Terrain/grassdiffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/grassdiffuse.png -------------------------------------------------------------------------------- /Render Terrain/grassnormalmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/grassnormalmap.png -------------------------------------------------------------------------------- /Render Terrain/grassnormals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/grassnormals.png -------------------------------------------------------------------------------- /Render Terrain/heightmap10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap10.png -------------------------------------------------------------------------------- /Render Terrain/heightmap2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap2.png -------------------------------------------------------------------------------- /Render Terrain/heightmap3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap3.png -------------------------------------------------------------------------------- /Render Terrain/heightmap4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap4.png -------------------------------------------------------------------------------- /Render Terrain/heightmap5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap5.png -------------------------------------------------------------------------------- /Render Terrain/heightmap6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap6.png -------------------------------------------------------------------------------- /Render Terrain/heightmap8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap8.png -------------------------------------------------------------------------------- /Render Terrain/heightmap9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/heightmap9.png -------------------------------------------------------------------------------- /Render Terrain/rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/rock.png -------------------------------------------------------------------------------- /Render Terrain/rockdepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/rockdepthmap.png -------------------------------------------------------------------------------- /Render Terrain/rockdiffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/rockdiffuse.png -------------------------------------------------------------------------------- /Render Terrain/rocknormalmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/rocknormalmap.png -------------------------------------------------------------------------------- /Render Terrain/rocknormals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/rocknormals.png -------------------------------------------------------------------------------- /Render Terrain/snowdepthmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/snowdepthmap.png -------------------------------------------------------------------------------- /Render Terrain/snowdiffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/snowdiffuse.png -------------------------------------------------------------------------------- /Render Terrain/snownormalmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/snownormalmap.png -------------------------------------------------------------------------------- /Render Terrain/snownormals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Traagen/Render-Terrain/293516eb66a59b2e2a4df205d696e7f5e9717df9/Render Terrain/snownormals.png --------------------------------------------------------------------------------