This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1764
views
15. Simple Lighting
To make our scenes more realistic and fun, we can use a technique called lighting. In this lesson, we will learn about the different kinds of light, and the math that is involved. At the end of the lesson, you will have learned how to impliment a simple lighting technique called Directional Lighting. Remember that the fixed function pipeline was taken out of directx 10, and structures and types used to impliment lighting in directx 9 were part of the fixed function pipeline. What this means, is lighting must now be done manualy in directx 10, making it a bit more tedious, but also giving you much more control over it.
I don't believe i need to stress the importance light has on the realistic look and feel of video games. As the fixed function pipeline was removed from directx 10, we now need to impliment our lights manually. Typically, the more accurate the lighting model is, the more computationally expensive and complex it is, and believe me, it can get REAL expensive. In video games, we need to find a way to impliment light while keeping our game running at 30 fps and above. When modeling scenes for a movie, since each frame is pre-rendered, they can afford to spend hours if not days rendering a single frame. In this lesson, we will learn how to impliment the simplest of lights, the directional light, also known as the parallel light. When doing the calculations to find the color when light is hitting a pixel, the color values in the color variable may be greater than 1 or less than zero. To fix this, we use a function in the HLSL language called saturate. ##Types of light## We can put the different kinds of lights into the following 4 groups: **Ambient** Ambient light is used so parts of our scene that are not touched directly by a light source do not end up pitch black. Ambient light is basically the lowest amount of light any part of our scene can have. In real life, ambient light is all that light that bounces around off every surface, lighting up the entire area, even if not all of the area is in the direct light from a light source. Like we said a bit ago, animated movies can afford to do complex lighting model which calculates all the light flying around a room lighting everything up, but in video games, we don't have the time for these calculations, so we use a simple hack which makes sure that all parts of our scene get a little bit of light so we can see everything. To find this value, we do a component wise multiplication of the ambient color of our light and the diffuse color of our material. eg. LightAmbient(0.5, 0.5, 0.5) * MaterialDiffuse(0.75, 1.0, 0.5) = (0.375, 0.5, 0.25) **Diffuse** To make things simple and performance friendly, we assume that when light strikes a surface, it is reflected off the surface at all angle equally. This way, we do not need to take the position of our camera into account. When we look at an object being hit by this kind of light, it doesn't matter what angle we look at it, it will still have the same amount of light bouncing off and entering our eye. To find this value, first we find out if the pixel is directly in the light from the light source and how much by using lambert's cosine law: inLight = max(lightDirection * surfaceNormal, 0), then we do a component wise multiplication of the diffuse value of our light, the diffuse value of our material, and how much the surface is in the light, if it is. eg. inLight = lightDirection(1.0, 1.0, 1.0) * surfaceNormal(1.0, 1.0, 1.0) = 1 (surfaceNormal and lightDirection are the same here to make it simple) 1 * LightDiffuse(1.0, 1.0, 1.0) * MaterialDiffuse(0.75, 1.0, 0.5) = (0.75, 1.0, 0.5) **Specular** This is the most expensive of the kinds of lights, but they can make a scene beautifuly realistic when used correctly. This is the one where you get a "glare" off a glossy object when the light source's light bounces directly off the glossy surface into your eye. We'll leave this one out of this lesson to keep it simple, and we'll cover it in a later lesson. **Emissive** Emissive light is different than the other light types as it doesn't actually send out light and light up other objects. Its the "glow" that lights up an object sending out light. An example of an object using this kind of light is a light bulb. In a video game, when you look at a turned on light bulb, you can see it glowing, but the glowing is independent of the light that the light bulb is actually sending out. ##Types of light sources## There are three different kinds of light sources: Directional, Spotlights, and Point Lights. **Directional Lights (A.K.A. Parallel Lights)** +[http://www.braynzarsoft.net/image/100145][directional light] Directional lights, also known as Parallel lights, are the simplest implimentation of a light. They have no source, only a direction and color. A good example of a directional light in the real world would be the sun. Although on a massive scale, the sun would be more like a point light. This is the type of lighting model we will use for this lesson. **Point Lights** +[http://www.braynzarsoft.net/image/100146][point light] Point Lights have a position, a fall off point or range, and a color. There is no need for a single direction in point lights, as they send light in all directions. They are very similar to directional lights, but the difference is they can have a fall off point, which means if something is too far away from the source of the pointlight, it will not receive light, and the other thing is, when computing directional light, the light vector used to find out if a surface is in the light is the same for all objects in a scene, but when using a pointlight, the light vector changes from point to point. for example, if you have a lightbulb in the middle of the room, and a box on either side of the room, the left side of the box on the right side of the room will be in the light, and the right side of the box on the left side of the room will be lit up. Using directional lighting, the same side of both boxes will be lit up (that was a little wordy, sorry). An example of a point light would be a light bulb, or on a massive scale, the sun. **Spotlights** +[http://www.braynzarsoft.net/image/100147][spot light] Spotlights are the most expensive light source, as they take the most to compute. They have a direction, a position, a color, two angles, and a fall off point or range. The reason for having two cones is to give us extra flexibility, as a point directly in the center of the cone should be brighter than a point on the edge of the cone. The inner cone is brighter than the outer cone, and there should be a falloff from the center to the edge of the cone. To find how bright a surface is in while inside the cone of light, if its actually inside, we can do the same type of calculation as we do with the specular light. However, to keep this lesson simple, we'll just cover these lights in a later lesson. An example of a Spotlight would be a flashlight. ##Normals## Normals are used to define which direction a surface is facing. Here we will use them to determine if the surface is in the light from a light source, and how much light it should recieve. Translating the vertices in world space makes the normal vector not of unit lenght, meaning the values in the normal vector are greater than 1 or less than zero. To fix this, we use a function in the HLSL language called normalize to fix them. **Vertex Normals** +[http://www.braynzarsoft.net/image/100148][vertex normals] We define a normal when defining a vertex. Then we use this normal for each pixel on the surface to dertermine how much light that pixel will recieve. However, if we were to create an object such as a sphere, we use a technique called normal averaging to make the sphere appear smooth, otherwise, if we did not use normal averaging, the entire surface of each triangle would be lit up in the light independent of the surrounding triangles, making the sphere look "choppy". **Face Normals** +[http://www.braynzarsoft.net/image/100149][face normals] Face normals are the same as vertex normals, but instead of defining the direction the vertex is facing, it defines the direction the surface is facing. We do not actually define face normals when creating an object, only vertex normals, then we average the vertex normals to make the face normal. **Normal Averaging** +[http://www.braynzarsoft.net/image/100150][normal averaging] Normal averaging is a technique used to make lighting look less "choppy". To get a normal average, we take the the normal of each surface using that vertex, and average them. Doing this, the light will gradually move accross each face, instead of an entire face at a time. First we define an effect variable which we will use to send information to the HLSL. This variable is the definition of the light. ID3D10EffectVariable* fxLightVar; As you can see, we have updated our vertex structure. We have included a vector defining a normal for our vertex. struct Vertex { Vertex(){} Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz) : pos(x,y,z), texCoord(u,v), normal(nx,ny,nz){} D3DXVECTOR3 pos; D3DXVECTOR2 texCoord; D3DXVECTOR3 normal; }; Here we have a new structure called Light. We will use this structure to hold information about our light. This structure is pretty much the same structure as the one we will create in our effect file. You may notice how we have a float variable called pad. We do this to match the C++ structure with the one in the effect file. HLSL packs structures into 4D vectors. You are not allowed to split a single variable between two 4D vectors. That is why we use the pad variable. Look at the code. the first member of the structure is a D3DXVECTOR3 type. This is a 3D vector, but remember how HLSL packs everything into 4D vectors? the next member below would be the D3DXCOLOR variable, which is basically a 4D vector. if we did not have the pad variable between, the first number of the ambient member would be packed into the 4D vector of the dir member when being sent to the HLSL. So to stop this from happening, we have the pad variable, so that will be packed into the 4th spot in the 4D vector containing the dir member. I hope that makes sense. After our Light structure, we define a Light type called light. struct Light { Light() { ZeroMemory(this, sizeof(Light)); } D3DXVECTOR3 dir; float pad; D3DXCOLOR ambient; D3DXCOLOR diffuse; }; Light light; Now we move down to the InitScene() function, where it starts by filling in our light structure. Play around with these variables, you can probably guess what each do. light.dir = D3DXVECTOR3(0.25f, 0.5f, -1.0f); light.ambient = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f); light.diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); We have modified our Vertex structure, so now we need to modify the part where we define our cube's vertices. The last three values of each vertex is the normal. You may wonder how i got these values for the normals. It might not always work out this well, but since we have defined the vertices of our cube around the central point (0,0,0), we can use the same values of the position of our vertices to define the normal of our vertices. Normally we will need to find some sort of algorithm to define each vertices normals, by using a technique called normal averaging. We will do this in a later lesson. // Front Face v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f); v[1] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-1.0f, 1.0f, -1.0f); v[2] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f); v[3] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f); // Back Face v[4] = Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f); v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f); v[6] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[7] = Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f,-1.0f, 1.0f, 1.0f); // Top Face v[8] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,-1.0f, 1.0f, -1.0f); v[9] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,-1.0f, 1.0f, 1.0f); v[10] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[11] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f); // Bottom Face v[12] = Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, -1.0f); v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f); v[14] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f); v[15] = Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,-1.0f, -1.0f, 1.0f); // Left Face v[16] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,-1.0f, -1.0f, 1.0f); v[17] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,-1.0f, 1.0f, 1.0f); v[18] = Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f,-1.0f, 1.0f, -1.0f); v[19] = Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, -1.0f); // Right Face v[20] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f); v[21] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f); v[22] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[23] = Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f); We have done this next line before, but with different values, so i hope you understand what this is doing. We have added a new line to our vertex element description, which includes the normal element. {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0} We have also done this before. We are binding the variable light from our effect file to the variables fxLightVar in our C++ code. fxLightVar = FX->GetVariableByName("light"); And the only thing we have changed here is the second parimeter from 2 to 3, because we have added another member to our vertex structure, the vertex normal. d3dDevice->CreateInputLayout( layout, 3, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); Now we go to the drawScene function, where we have our last new line of code in our C++ file. What this line does is update the variable light in our effect file with a pointer to our light structure. fxLightVar->SetRawValue(&light, 0, sizeof(Light)); Lets go to our effect file. We have added a structure, called light. This structure will be used to define our light. As you can see, it matches the Light structure we have created in our C++ file. struct Light { float3 dir; float4 ambient; float4 diffuse; }; We have added a new cbuffer too. This cbuffer will be updated per frame. Remember back when we talked about the cbuffers and how we should organize them by how often they are updated. In this cbuffer, we have our light variable, which will be updated every frame. cbuffer cbPerFrame { Light light; }; This is our modified Vertex Shader output structure. All we have done here is add a 3D float called normal so our C++ code can send the vertice's normal information to the vertex shader and pixel shader. struct VS_OUTPUT //output structure for vertex shader { float4 Pos : SV_POSITION; float2 texCoord : TEXCOORD; float3 normal : NORMAL; }; Here we have a new parimeter for our vertex shader. The normal, which we just talked about above. VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL) { The only new line in our vertex shader is the line which translates our vertex normal information to WVP space. output.normal = mul(normal, WVP); Now down to our Pixel shader, where the first new line normalizes our vertex normal after it has been translated to world space. When translating normals, you must always normalize them since they will not be of unit lenght after translating, as in, the values may be higher than 1.0f or lower than -1.0f. input.normal = normalize(input.normal); Now we define the color of our pixel. The first line creates a new float3 called finalColor to hold our color in. The next line is using the ambient color value we gave our light and multiplying it with the diffuse value of the current pixel to get the color of that pixel before the direct light from the directional light hits the pixel. After that, we use the light equation and add it to the final color to get the final color of our pixel after the light has or has not touched the pixel directly. Doing calculations with the light and colors may make the color values greater than 1 or less than 0. To fix this we use the function saturate. float3 finalColor; finalColor = diffuse * light.ambient; finalColor += saturate(dot(light.dir, input.normal) * light.diffuse * diffuse); Finally we return the final color packed into a 4D float with the alpha value of our diffuse variable. return float4(finalColor, diffuse.a); This is a very simple and performance friendly light technique, but i wanted to give you a lighting lesson before we moved on to loading a 3d model, direct input, direct sound, and first and 3rd person cameras. Hope you enjoyed the lesson, i'll have more soon! so stick around! Here's the final code: main.cpp #include <Windows.h> #include <d3d10.h> #include <d3dx10.h> #include <string> #pragma comment(lib, "D3D10.lib") #pragma comment(lib, "d3dx10d.lib") LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 800; const int Height = 600; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); HRESULT hr; ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; ID3D10Effect* FX; ID3D10InputLayout* VertexLayout; ID3D10Buffer* VertexBuffer; ID3D10Buffer* IndexBuffer; ID3D10EffectTechnique* Technique; ID3D10DepthStencilView* DepthStencilView; ID3D10Texture2D* DepthStencilBuffer; ID3D10ShaderResourceView* DiffuseMapResourceView; ID3D10EffectShaderResourceVariable* fxDiffuseMapVar; ID3D10EffectMatrixVariable* fxWVPVar; //////////////////////////////new///////////////////////////////////////////////////////// ID3D10EffectVariable* fxLightVar; //////////////////////////////new///////////////////////////////////////////////////////// D3DXMATRIX WVP; D3DXMATRIX World; D3DXMATRIX View; D3DXMATRIX Projection; D3DXVECTOR3 Position; D3DXVECTOR3 Target; D3DXVECTOR3 Up; D3DXMATRIX Rotation; D3DXMATRIX Scale; D3DXMATRIX Translation; D3DXMATRIX Transformations; float rot = 0.01f; bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitScene(); void DrawScene(); bool ReleaseObjects(); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); struct Vertex { //////////////////////////////new///////////////////////////////////////////////////////// Vertex(){} Vertex(float x, float y, float z, float u, float v, float nx, float ny, float nz) : pos(x,y,z), texCoord(u,v), normal(nx,ny,nz){} D3DXVECTOR3 pos; D3DXVECTOR2 texCoord; D3DXVECTOR3 normal; //////////////////////////////new///////////////////////////////////////////////////////// }; //////////////////////////////new///////////////////////////////////////////////////////// struct Light { Light() { ZeroMemory(this, sizeof(Light)); } D3DXVECTOR3 dir; float pad; D3DXCOLOR ambient; D3DXCOLOR diffuse; }; Light light; //////////////////////////////new///////////////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); if(!ReleaseObjects()) { MessageBox(0, L"Object Releasing - Failed", L"Error", MB_OK); return 0; } return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Window Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3dApp(HINSTANCE hInstance) { UINT createDeviceFlags = 0; D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] ); DXGI_SWAP_CHAIN_DESC scd; scd.BufferDesc.Width = Width; scd.BufferDesc.Height = Height; scd.BufferDesc.RefreshRate.Numerator = 60; scd.BufferDesc.RefreshRate.Denominator = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //no multisampling scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 1; scd.OutputWindow = hwnd; scd.Windowed = true; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; D3D10CreateDeviceAndSwapChain(0, D3D10_DRIVER_TYPE_HARDWARE, 0, 0, D3D10_SDK_VERSION, &scd, &SwapChain, &d3dDevice); ID3D10Texture2D* backBuffer; SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer)); d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); D3D10_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D10_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; d3dDevice->CreateTexture2D(&depthStencilDesc, NULL, &DepthStencilBuffer); d3dDevice->CreateDepthStencilView(DepthStencilBuffer, NULL, &DepthStencilView); d3dDevice->OMSetRenderTargets(1, &RenderTargetView, DepthStencilView); // Setup the viewport D3D10_VIEWPORT vp; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; d3dDevice->RSSetViewports( 1, &vp ); D3DXMatrixIdentity( &World ); Position = D3DXVECTOR3( 0.0f, 6.0f, -14.0f ); Target = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); Up = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); D3DXMatrixLookAtLH( &View, &Position, &Target, &Up ); return true; } bool InitScene() { //////////////////////////////new///////////////////////////////////////////////////////// light.dir = D3DXVECTOR3(0.25f, 0.5f, -1.0f); light.ambient = D3DXCOLOR(0.2f, 0.2f, 0.2f, 1.0f); light.diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); //////////////////////////////new///////////////////////////////////////////////////////// Vertex v[24]; //////////////////////////////new///////////////////////////////////////////////////////// // Front Face v[0] = Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f,-1.0f, -1.0f, -1.0f); v[1] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f,-1.0f, 1.0f, -1.0f); v[2] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f); v[3] = Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f); // Back Face v[4] = Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f); v[5] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f); v[6] = Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[7] = Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f,-1.0f, 1.0f, 1.0f); // Top Face v[8] = Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f,-1.0f, 1.0f, -1.0f); v[9] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,-1.0f, 1.0f, 1.0f); v[10] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[11] = Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f); // Bottom Face v[12] = Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, -1.0f); v[13] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f); v[14] = Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f); v[15] = Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f,-1.0f, -1.0f, 1.0f); // Left Face v[16] = Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f,-1.0f, -1.0f, 1.0f); v[17] = Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f,-1.0f, 1.0f, 1.0f); v[18] = Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f,-1.0f, 1.0f, -1.0f); v[19] = Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, -1.0f); // Right Face v[20] = Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f); v[21] = Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f); v[22] = Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f); v[23] = Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f); //////////////////////////////new///////////////////////////////////////////////////////// D3D10_BUFFER_DESC vbd; vbd.Usage = D3D10_USAGE_IMMUTABLE; vbd.ByteWidth = sizeof(Vertex) * 24; vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER; vbd.CPUAccessFlags = 0; vbd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA vinitData; vinitData.pSysMem = v; d3dDevice->CreateBuffer(&vbd, &vinitData, &VertexBuffer); DWORD i[36]; // Front Face i[0] = 0; i[1] = 1; i[2] = 2; i[3] = 0; i[4] = 2; i[5] = 3; // Back Face i[6] = 4; i[7] = 5; i[8] = 6; i[9] = 4; i[10] = 6; i[11] = 7; // Top Face i[12] = 8; i[13] = 9; i[14] = 10; i[15] = 8; i[16] = 10; i[17] = 11; // Bottom Face i[18] = 12; i[19] = 13; i[20] = 14; i[21] = 12; i[22] = 14; i[23] = 15; // Left Face i[24] = 16; i[25] = 17; i[26] = 18; i[27] = 16; i[28] = 18; i[29] = 19; // Right Face i[30] = 20; i[31] = 21; i[32] = 22; i[33] = 20; i[34] = 22; i[35] = 23; D3D10_BUFFER_DESC ibd; ibd.Usage = D3D10_USAGE_IMMUTABLE; ibd.ByteWidth = sizeof(DWORD) * 36; ibd.BindFlags = D3D10_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = i; d3dDevice->CreateBuffer(&ibd, &iinitData, &IndexBuffer); UINT stride = sizeof( Vertex ); UINT offset = 0; d3dDevice->IASetVertexBuffers( 0, 1, &VertexBuffer, &stride, &offset ); d3dDevice->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0); D3D10_INPUT_ELEMENT_DESC layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0}, {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}, //////////////////////////////new///////////////////////////////////////////////////////// {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0} //////////////////////////////new///////////////////////////////////////////////////////// }; ID3D10Blob* compilationErrors = 0; HRESULT hr = 0; hr = D3DX10CreateEffectFromFile( L"vertex.fx", NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, d3dDevice, NULL, NULL, &FX, &compilationErrors, NULL ); if(FAILED(hr)) { MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0); compilationErrors->Release(); return false; } D3DX10CreateShaderResourceViewFromFile(d3dDevice, L"braynzar.jpg", 0, 0, &DiffuseMapResourceView, 0 ); Technique = FX->GetTechniqueByName( "Tech" ); fxWVPVar = FX->GetVariableByName("WVP")->AsMatrix(); fxDiffuseMapVar = FX->GetVariableByName("DiffuseMap")->AsShaderResource(); //////////////////////////////new///////////////////////////////////////////////////////// fxLightVar = FX->GetVariableByName("light"); //////////////////////////////new///////////////////////////////////////////////////////// D3D10_PASS_DESC PassDesc; Technique->GetPassByIndex( 0 )->GetDesc( &PassDesc ); //////////////////////////////new///////////////////////////////////////////////////////// d3dDevice->CreateInputLayout( layout, 3, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); //////////////////////////////new///////////////////////////////////////////////////////// d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); return true; } bool ReleaseObjects() { if( d3dDevice ) d3dDevice->ClearState(); if( VertexBuffer ) VertexBuffer->Release(); if( IndexBuffer ) IndexBuffer->Release(); if( VertexLayout ) VertexLayout->Release(); if( FX ) FX->Release(); if( RenderTargetView ) RenderTargetView->Release(); if( SwapChain ) SwapChain->Release(); if( d3dDevice ) d3dDevice->Release(); if( DiffuseMapResourceView ) DiffuseMapResourceView->Release(); return true; } void DrawScene() { //Draw Scene Here D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f); d3dDevice->ClearRenderTargetView( RenderTargetView, bgColor ); d3dDevice->ClearDepthStencilView(DepthStencilView, D3D10_CLEAR_DEPTH|D3D10_CLEAR_STENCIL, 1.0f, 0); D3DXMatrixPerspectiveFovLH(&Projection, 0.4f*3.14f, Width/Height, 1.0f, 1000.0f); D3DXVECTOR3 rotaxis(0.0f, 1.0f, 0.0f); D3DXMatrixRotationAxis(&Rotation, &rotaxis, rot); D3DXMatrixTranslation( &Translation, 0.0f, 0.0f, 4.0f ); Transformations =Translation * Rotation; rot += .0005f; WVP = World * Transformations * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); fxDiffuseMapVar->SetResource(DiffuseMapResourceView); //////////////////////////////new///////////////////////////////////////////////////////// fxLightVar->SetRawValue(&light, 0, sizeof(Light)); //////////////////////////////new///////////////////////////////////////////////////////// D3D10_TECHNIQUE_DESC techDesc; Technique->GetDesc( &techDesc ); //draw first cube for( UINT p = 0; p < techDesc.Passes; ++p ) { Technique->GetPassByIndex( p )->Apply( 0 ); d3dDevice->DrawIndexed(36, 0, 0); } D3DXMatrixRotationAxis(&Rotation, &rotaxis, -rot); D3DXMatrixScaling( &Scale, 1.3f, 1.3f, 1.3f ); Transformations = Rotation * Scale; WVP = World * Transformations * View * Projection; fxWVPVar->SetMatrix((float*)&WVP); //draw second cube for( UINT p = 0; p < techDesc.Passes; ++p ) { Technique->GetPassByIndex( p )->Apply( 0 ); d3dDevice->DrawIndexed(36, 0, 0); } SwapChain->Present( 0, 0 ); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ if(MessageBox(0, L"Are you sure you want to exit?", L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } vertex.fx struct Light { float3 dir; float4 ambient; float4 diffuse; }; cbuffer cbPerFrame { Light light; }; cbuffer cbPerObject { float4x4 WVP; }; Texture2D DiffuseMap; SamplerState TriLinearSample { Filter = MIN_MAG_MIP_LINEAR; }; struct VS_OUTPUT //output structure for vertex shader { float4 Pos : SV_POSITION; float2 texCoord : TEXCOORD; float3 normal : NORMAL; }; // Vertex Shader VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD, float3 normal : NORMAL) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = mul(inPos, WVP); output.normal = mul(normal, WVP); output.texCoord = inTexCoord; return output; //send color and position to pixel shader } // Pixel Shader float4 PS(VS_OUTPUT input) : SV_Target { input.normal = normalize(input.normal); float4 diffuse = DiffuseMap.Sample( TriLinearSample, input.texCoord ); float3 finalColor; finalColor = diffuse * light.ambient; finalColor += saturate(dot(light.dir, input.normal) * light.diffuse * diffuse); return float4(finalColor, diffuse.a); } technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } }
Sign in to comment