Sign in to follow this  

DX11 Creating a Shader class

This topic is 1574 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello! I am working on a class for my game engine called NXShader which will allow me to load my shaders easily like this:

NXShader(HWND*, WCHAR*, const string&, const string&);

The next thing I would like to add is the ability to dynamically set shader parameters if that makes any sense. Kind of similar to how shader parameters were handled in XNA:

Effect.Parameters["parameter"].SetValue(value); 

I want to be able to do something like this with my NXShader class:

NXShader* shader = new NXShader(nullptr, L"Texture.fx", "VS", "PS");
shader->SetParameter(parameter name, value);

But I am not actually sure about to do this. The class is currently set up like this:

Header:

struct MatrixBufferType
	{
		XMMATRIX World;
		XMMATRIX View;
		XMMATRIX Projection;
	};

	struct NEXENGINE_API NXShader
	{
		public:
			NXShader(){}
			NXShader(HWND*, WCHAR*, const string&, const string&);
			HRESULT Initialize(HWND*);
			bool SetShaderParameters(XMMATRIX&, XMMATRIX&, XMMATRIX&);
			bool SetShaderParameters(XMMATRIX&, XMMATRIX&, XMMATRIX&, NXTexture2D*);
			void Dispose();

			HRESULT CompileShaderFromFile(WCHAR*, LPCSTR, LPCSTR, ID3DBlob**);

			WCHAR* TargetProfile = L"_4_0";
			std::string VertexShaderEntry;
			std::string PixelShaderEntry;

			WCHAR* Name;
			ID3D11VertexShader* VertexShader;
			ID3D11PixelShader* PixelShader;
			ID3D11InputLayout* Layout;
			ID3D11Buffer* MatrixBuffer;
	};

Source:

#include "stdafx.h"
#include "NXShader.h"
#include <d3dcompiler.h>
#include "NXTypes.h"

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")

namespace NexEngine
{
	NXShader::NXShader(HWND* wnd, WCHAR* shaderName, const string& vsEntry, const string& psEntry)
	{
		Name = shaderName;
		VertexShaderEntry = vsEntry;
		VertexShader = 0;
		PixelShaderEntry = psEntry;
		PixelShader = 0;
		Layout = 0;
		MatrixBuffer = 0;

		HRESULT result;

		result = Initialize(wnd);
		if (FAILED(result))
			return;
	}

	HRESULT NXShader::Initialize(HWND* hwnd)
	{
		HRESULT result;
		D3D11_BUFFER_DESC matrixBufferDesc;
		ID3DBlob* pVSBlob = nullptr;
		result = CompileShaderFromFile(Name, VertexShaderEntry.c_str(), "vs_4_0", &pVSBlob);
		if (FAILED(result))
		{
			MessageBox(nullptr,
				L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
			exit(0);
			return result;
		}

		result = NXGPU::GetDevice()->CreateVertexShader(pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), nullptr, &VertexShader);
		if (FAILED(result))
		{
			pVSBlob->Release();
			MessageBox(nullptr, L"Failed To Create Vertex Shader.", L"Error", MB_OK);
			return result;
		}

		ID3DBlob* pPSBlob = nullptr;
		result = CompileShaderFromFile(Name, PixelShaderEntry.c_str(), "ps_4_0", &pPSBlob);
		if (FAILED(result))
		{
			MessageBox(nullptr,
				L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
			exit(0);
			return result;
		}

		result = NXGPU::GetDevice()->CreatePixelShader(pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), nullptr, &PixelShader);
		pPSBlob->Release();
		if (FAILED(result))
		{
			MessageBox(nullptr, L"Failed To Create Pixel Shader.", L"Error", MB_OK);
			return result;
		}
		pPSBlob = 0;

		result = NXGPU::GetDevice()->CreateInputLayout(NXVertex::InputElements, NXVertex::InputElementCount, pVSBlob->GetBufferPointer(),
			pVSBlob->GetBufferSize(), &Layout);


		pVSBlob->Release();
		if (FAILED(result))
		{
			MessageBox(nullptr, L"Failed To Create Input Layout.", L"Error", MB_OK);
			exit(0);
			return result;
		}
		pVSBlob = 0;
		
		matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
		matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
		matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		matrixBufferDesc.MiscFlags = 0;
		matrixBufferDesc.StructureByteStride = 0;

		result = NXGPU::GetDevice()->CreateBuffer(&matrixBufferDesc, NULL, &MatrixBuffer);
		if (FAILED(result))
		{
			MessageBox(nullptr, L"Faield to Create Matrix Buffer", L"Error", MB_OK);
			exit(0);
			return result;
		}

		return S_OK;
	}

	bool NXShader::SetShaderParameters(XMMATRIX& view, XMMATRIX& world, XMMATRIX& proj)
	{
		HRESULT result;
		D3D11_MAPPED_SUBRESOURCE mappedResource;
		MatrixBufferType* dataPtr;
		unsigned int bufferNumber;

		world = XMMatrixTranspose(world);
		view = XMMatrixTranspose(view);
		proj = XMMatrixTranspose(proj);

		result = NXGPU::GetDeviceContext()->Map(MatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
		if (FAILED(result))
		{
			return false;
		}

		dataPtr = (MatrixBufferType*) mappedResource.pData;

		dataPtr->World = world;
		dataPtr->View = view;
		dataPtr->Projection = proj;

		NXGPU::GetDeviceContext()->Unmap(MatrixBuffer, 0);

		bufferNumber = 0;

		NXGPU::GetDeviceContext()->VSSetConstantBuffers(bufferNumber, 1, &MatrixBuffer);

		return true;
	}

	bool NXShader::SetShaderParameters(XMMATRIX& view, XMMATRIX& world, XMMATRIX& proj, NXTexture2D* texture)
	{
		HRESULT result;
		D3D11_MAPPED_SUBRESOURCE mappedResource;
		MatrixBufferType* dataPtr;
		unsigned int bufferNumber;

		world = XMMatrixTranspose(world);
		view = XMMatrixTranspose(view);
		proj = XMMatrixTranspose(proj);

		result = NXGPU::GetDeviceContext()->Map(MatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
		if (FAILED(result))
		{
			return false;
		}

		dataPtr = (MatrixBufferType*) mappedResource.pData;

		dataPtr->World = world;
		dataPtr->View = view;
		dataPtr->Projection = proj;

		NXGPU::GetDeviceContext()->Unmap(MatrixBuffer, 0);

		bufferNumber = 0;

		NXGPU::GetDeviceContext()->VSSetConstantBuffers(bufferNumber, 1, &MatrixBuffer);

		NXGPU::GetDeviceContext()->PSSetShaderResources(0, 1, &texture->Texture);

		return true;
	}

	void NXShader::Dispose()
	{
		if (MatrixBuffer)
		{
			MatrixBuffer->Release();
			MatrixBuffer = 0;
		}

		if (Layout)
		{
			Layout->Release();
			Layout = 0;
		}

		if (PixelShader)
		{
			PixelShader->Release();
			PixelShader = 0;
		}

		if (VertexShader)
		{
			VertexShader->Release();
			VertexShader = 0;
		}

		return;
	}

	HRESULT NXShader::CompileShaderFromFile(WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
	{
		HRESULT hr = S_OK;

		DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
		// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
		// Setting this flag improves the shader debugging experience, but still allows 
		// the shaders to be optimized and to run exactly the way they will run in 
		// the release configuration of this program.
		dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

		ID3DBlob* pErrorBlob;
		hr = D3DCompileFromFile(szFileName, nullptr, nullptr, szEntryPoint, szShaderModel,
			dwShaderFlags, 0, ppBlobOut, &pErrorBlob);
		if (FAILED(hr))
		{
			if (pErrorBlob != nullptr)
				OutputDebugStringA((char*) pErrorBlob->GetBufferPointer());
			if (pErrorBlob) pErrorBlob->Release();
			return hr;
		}
		if (pErrorBlob) pErrorBlob->Release();

		return S_OK;
	}
} 

I would like to remove the SetShaderParameter bool methods above, and replace them with something like this:

void SetParameter(const string&, float);
void SetParameter(const string&, int);
void SetParameter(const string&, XMMatrix*)
void SetParameter...etc.

This would really help me out. The class can currently compile and load Pixel/Vertex shaders correctly:

kum0.png

 

But I seem to limited to setting parameters by creating a bunch of constant buffers for each cbuffer in my shaders. Is there a way to do what I am trying to do in DX11? Thanks! smile.png

Edited by KoldGames

Share this post


Link to post
Share on other sites
Sure, there's a way. You could create a mapping from string to a structure member.

There's really no reason to go that route though. Event the DX9 effects framework essentially uses cbuffers on the back end, with the strings just being mappings.

You really shouldn't be using strings for this though, its not a particularly efficient idea.

Share this post


Link to post
Share on other sites

The way shader constants have changed from D3D9 to 11, and the fact that D3D, even in version 9, never really had per-shader constants (they were always global state, the old Effects framework just never exposed that well), means that you're probably better off separating your constants from your shaders, otherwise you've got an artificial coupling that's only going to hinder you as you go forward.

 

As an example, what would you propose to do if you had two different vertex shaders but they both needed to use the same matrices?  Your current design doesn't handle this requirement, instead forcing you to upload the matrices twice.  However, D3D itself doesn't (and never did) require you to do that.

 

If what you really want is just a D3D11 version of the old Effects framework (which it looks like is what you're designing towards), there is one provided with the DirectX SDK (I don't know about newer Windows SDK versions) in source-code-only form; you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11 and you can compile it and link to it.

 

Personally I've just got a much more lightweight shader class that just contains creation and destruction code for the types of shaders (and input layouts) I'm interested in, using a hash of the bytecode and a global shader cache to avoid creating the same object more than once, some simple state change management, and otherwise I handle cbuffer updates completely separately and via the raw API.

Edited by mhagain

Share this post


Link to post
Share on other sites

Hey, thanks for the replies!

Sure, there's a way. You could create a mapping from string to a structure member.

There's really no reason to go that route though. Event the DX9 effects framework essentially uses cbuffers on the back end, with the strings just being mappings.

You really shouldn't be using strings for this though, its not a particularly efficient idea.

What would you recommend to replace the strings? Is WCHAR or char good?

 

The way shader constants have changed from D3D9 to 11, and the fact that D3D, even in version 9, never really had per-shader constants (they were always global state, the old Effects framework just never exposed that well), means that you're probably better off separating your constants from your shaders, otherwise you've got an artificial coupling that's only going to hinder you as you go forward.

 

As an example, what would you propose to do if you had two different vertex shaders but they both needed to use the same matrices?  Your current design doesn't handle this requirement, instead forcing you to upload the matrices twice.  However, D3D itself doesn't (and never did) require you to do that.

 

If what you really want is just a D3D11 version of the old Effects framework (which it looks like is what you're designing towards), there is one provided with the DirectX SDK (I don't know about newer Windows SDK versions) in source-code-only form; you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11 and you can compile it and link to it.

 

Personally I've just got a much more lightweight shader class that just contains creation and destruction code for the types of shaders (and input layouts) I'm interested in, using a hash of the bytecode and a global shader cache to avoid creating the same object more than once, some simple state change management, and otherwise I handle cbuffer updates completely separately and via the raw API.

Hey! Thanks for the info! It is really helpful. smile.png

Edited by KoldGames

Share this post


Link to post
Share on other sites

The way I do it in my engine is that I add meta data for my shaders, describing the size of the constant, it's register, it's name, whatever information I may need. Then in my game I have a SetUniform call that takes in a name or an enum that corresponds to known constants, a void*, and the size of the uniform. Then I map the name/id to a global shader cache, and apply it from there. It takes  a bit of setup to work, but that's all handled by tools now. And I can create custom uniforms without having to cram it into some sort of pre-defined register index.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Similar Content

    • By mister345
      Hi, can somebody please tell me in clear simple steps how to debug and step through an hlsl shader file?
      I already did Debug > Start Graphics Debugging > then captured some frames from Visual Studio and
      double clicked on the frame to open it, but no idea where to go from there.
       
      I've been searching for hours and there's no information on this, not even on the Microsoft Website!
      They say "open the  Graphics Pixel History window" but there is no such window!
      Then they say, in the "Pipeline Stages choose Start Debugging"  but the Start Debugging option is nowhere to be found in the whole interface.
      Also, how do I even open the hlsl file that I want to set a break point in from inside the Graphics Debugger?
       
      All I want to do is set a break point in a specific hlsl file, step thru it, and see the data, but this is so unbelievably complicated
      and Microsoft's instructions are horrible! Somebody please, please help.
       
       
       

    • By mister345
      I finally ported Rastertek's tutorial # 42 on soft shadows and blur shading. This tutorial has a ton of really useful effects and there's no working version anywhere online.
      Unfortunately it just draws a black screen. Not sure what's causing it. I'm guessing the camera or ortho matrix transforms are wrong, light directions, or maybe texture resources not being properly initialized.  I didnt change any of the variables though, only upgraded all types and functions DirectX3DVector3 to XMFLOAT3, and used DirectXTK for texture loading. If anyone is willing to take a look at what might be causing the black screen, maybe something pops out to you, let me know, thanks.
      https://github.com/mister51213/DX11Port_SoftShadows
       
      Also, for reference, here's tutorial #40 which has normal shadows but no blur, which I also ported, and it works perfectly.
      https://github.com/mister51213/DX11Port_ShadowMapping
       
    • By xhcao
      Is Direct3D 11 an api function like glMemoryBarrier in OpenGL? For example, if binds a texture to compute shader, compute shader writes some values to texture, then dispatchCompute, after that, read texture content to CPU side. I know, In OpenGL, we could call glMemoryBarrier before reading to assure that texture all content has been updated by compute shader.
      How to handle incoherent memory access in Direct3D 11? Thank you.
    • By _Engine_
      Atum engine is a newcomer in a row of game engines. Most game engines focus on render
      techniques in features list. The main task of Atum is to deliver the best toolset; that’s why,
      as I hope, Atum will be a good light weighted alternative to Unity for indie games. Atum already
      has fully workable editor that has an ability to play test edited scene. All system code has
      simple ideas behind them and focuses on easy to use functionality. That’s why code is minimized
      as much as possible.
      Currently the engine consists from:
      - Scene Editor with ability to play test edited scene;
      - Powerful system for binding properties into the editor;
      - Render system based on DX11 but created as multi API; so, adding support of another GAPI
        is planned;
      - Controls system based on aliases;
      - Font system based on stb_truetype.h;
      - Support of PhysX 3.0, there are samples in repo that use physics;
      - Network code which allows to create server/clinet; there is some code in repo which allows
        to create a simple network game
      I plan to use this engine in multiplayer game - so, I definitely will evolve the engine. Also
      I plan to add support for mobile devices. And of course, the main focus is to create a toolset
      that will ease games creation.
      Link to repo on source code is - https://github.com/ENgineE777/Atum
      Video of work process in track based editor can be at follow link: 
       
       

    • By mister345
      I made a spotlight that
      1. Projects 3d models onto a render target from each light POV to simulate shadows
      2. Cuts a circle out of the square of light that has been projected onto the render target
      as a result of the light frustum, then only lights up the pixels inside that circle 
      (except the shadowed parts of course), so you dont see the square edges of the projected frustum.
       
      After doing an if check to see if the dot product of light direction and light to vertex vector is greater than .95
      to get my initial cutoff, I then multiply the light intensity value inside the resulting circle by the same dot product value,
      which should range between .95 and 1.0.
       
      This should give the light inside that circle a falloff from 100% lit to 0% lit toward the edge of the circle. However,
      there is no falloff. It's just all equally lit inside the circle. Why on earth, I have no idea. If someone could take a gander
      and let me know, please help, thank you so much.
      float CalculateSpotLightIntensity(     float3 LightPos_VertexSpace,      float3 LightDirection_WS,      float3 SurfaceNormal_WS) {     //float3 lightToVertex = normalize(SurfacePosition - LightPos_VertexSpace);     float3 lightToVertex_WS = -LightPos_VertexSpace;          float dotProduct = saturate(dot(normalize(lightToVertex_WS), normalize(LightDirection_WS)));     // METALLIC EFFECT (deactivate for now)     float metalEffect = saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace)));     if(dotProduct > .95 /*&& metalEffect > .55*/)     {         return saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace)));         //return saturate(dot(SurfaceNormal_WS, normalize(LightPos_VertexSpace))) * dotProduct;         //return dotProduct;     }     else     {         return 0;     } } float4 LightPixelShader(PixelInputType input) : SV_TARGET {     float2 projectTexCoord;     float depthValue;     float lightDepthValue;     float4 textureColor;     // Set the bias value for fixing the floating point precision issues.     float bias = 0.001f;     // Set the default output color to the ambient light value for all pixels.     float4 lightColor = cb_ambientColor;     /////////////////// NORMAL MAPPING //////////////////     float4 bumpMap = shaderTextures[4].Sample(SampleType, input.tex);     // Expand the range of the normal value from (0, +1) to (-1, +1).     bumpMap = (bumpMap * 2.0f) - 1.0f;     // Change the COORDINATE BASIS of the normal into the space represented by basis vectors tangent, binormal, and normal!     float3 bumpNormal = normalize((bumpMap.x * input.tangent) + (bumpMap.y * input.binormal) + (bumpMap.z * input.normal));     //////////////// LIGHT LOOP ////////////////     for(int i = 0; i < NUM_LIGHTS; ++i)     {     // Calculate the projected texture coordinates.     projectTexCoord.x =  input.vertex_ProjLightSpace[i].x / input.vertex_ProjLightSpace[i].w / 2.0f + 0.5f;     projectTexCoord.y = -input.vertex_ProjLightSpace[i].y / input.vertex_ProjLightSpace[i].w / 2.0f + 0.5f;     if((saturate(projectTexCoord.x) == projectTexCoord.x) && (saturate(projectTexCoord.y) == projectTexCoord.y))     {         // Sample the shadow map depth value from the depth texture using the sampler at the projected texture coordinate location.         depthValue = shaderTextures[6 + i].Sample(SampleTypeClamp, projectTexCoord).r;         // Calculate the depth of the light.         lightDepthValue = input.vertex_ProjLightSpace[i].z / input.vertex_ProjLightSpace[i].w;         // Subtract the bias from the lightDepthValue.         lightDepthValue = lightDepthValue - bias;         float lightVisibility = shaderTextures[6 + i].SampleCmp(SampleTypeComp, projectTexCoord, lightDepthValue );         // Compare the depth of the shadow map value and the depth of the light to determine whether to shadow or to light this pixel.         // If the light is in front of the object then light the pixel, if not then shadow this pixel since an object (occluder) is casting a shadow on it.             if(lightDepthValue < depthValue)             {                 // Calculate the amount of light on this pixel.                 float lightIntensity = saturate(dot(bumpNormal, normalize(input.lightPos_LS[i])));                 if(lightIntensity > 0.0f)                 {                     // Determine the final diffuse color based on the diffuse color and the amount of light intensity.                     float spotLightIntensity = CalculateSpotLightIntensity(                         input.lightPos_LS[i], // NOTE - this is NOT NORMALIZED!!!                         cb_lights[i].lightDirection,                          bumpNormal/*input.normal*/);                     lightColor += cb_lights[i].diffuseColor*spotLightIntensity* .18f; // spotlight                     //lightColor += cb_lights[i].diffuseColor*lightIntensity* .2f; // square light                 }             }         }     }     // Saturate the final light color.     lightColor = saturate(lightColor);    // lightColor = saturate( CalculateNormalMapIntensity(input, lightColor, cb_lights[0].lightDirection));     // TEXTURE ANIMATION -  Sample pixel color from texture at this texture coordinate location.     input.tex.x += textureTranslation;     // BLENDING     float4 color1 = shaderTextures[0].Sample(SampleTypeWrap, input.tex);     float4 color2 = shaderTextures[1].Sample(SampleTypeWrap, input.tex);     float4 alphaValue = shaderTextures[3].Sample(SampleTypeWrap, input.tex);     textureColor = saturate((alphaValue * color1) + ((1.0f - alphaValue) * color2));     // Combine the light and texture color.     float4 finalColor = lightColor * textureColor;     /////// TRANSPARENCY /////////     //finalColor.a = 0.2f;     return finalColor; }  
      Light_vs.hlsl
      Light_ps.hlsl
  • Popular Now