• Advertisement
Sign in to follow this  

DX11 DX11 - HLSL - Parallax Mapping

This topic is 1714 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

Now I've been trying to implement parallax mapping for a short while now, and I'm not quite sure if the result is right. The result (look at the plane):

 

1q04cp.png

 

Now there I have shown you the result of Diffuse, Parallax, Parallax with Normal Mapping rendering, and a blank space of course!

 

Now to some code:

 

(Relevant) Vertex Shader:

 

float binormal = cross(tangent,normal);

float3x3 tbnmatrix = transpose(float3x3(tangent,binormal,normal));
output.toeyetangent = mul((eyepos - output.position),tbnmatrix);

 

 

(Relevant) Pixel Shader:

 

float height = parallaxmap.Sample(ss, texCoord);

float3 toeyetangent = normalize(input.toeyetangent);
float2 offset = toeyetangent.xy*height*0.04f;

texCoord += offset;

 

 

And If you wan't, the full shader:

 

cbuffer ConstantObjectBuffer : register (b0)
{
	matrix worldMatrix; 
	matrix viewMatrix; 
	matrix projectionMatrix; 

	float4x4 rotation;
	float4 ambientcol;
	float texture_repeat;
	float3 tr_sace;
	float4 clipColor;
	float3 _instancePos;
	float inSPACE;
};

cbuffer ConstantFrameBuffer : register (b1)
{
	float4 lightvec;
	float4 lightcol;
	float time;
	float _wireframe;
	float2 wffspace;

	matrix tmat;
	matrix svp;
};

cbuffer UberData : register (b2)
{
	float _diffuse;
	float _texture;
	float _opacityEnable;
	float _directionall;
	float _bumpmap;
	float _fog;
	float _wind;
	float _pixelDisorter;
	float _disorterAmount;
	float _specular;
	float _doClip;
	float _terrain;
	float _instance;
	float _selected;
	float _windspeed;
	float _windamount;
	float _particle;
	float _shadows;
	float _envreflect;
	float _invDoClip;
	float _parallaxMapping;
	float3 ssspace;
};

cbuffer ConstantMatrixBuffer : register (b3)
{
	float4x4 world;
	float4x4 view;
	float4x4 projection;
	float4 eyepos;
	float3 cforward;
	float _space;
};

cbuffer ConstantFogBuffer : register (b4)
{
	float FogStart;
	float FogEnd;
	float2 __space;
	float3 FogColor;
	float __space2;
};

cbuffer ConstantSpecularBuffer : register (b5)
{
	float specularPower;
	float3 ____space;
    float4 specularColor;
};

cbuffer ConstantTerrainBuffer : register (b6)
{
	float t_nchannels;
	float3 t_space;
	float4 slopeActivation;
};

cbuffer ConstantParticleBuffer : register (b7)
{
	float particle_pastTime;
	float particle_lifeTime;
	float particle_fallOff;
	float _cpbspace;
};

cbuffer ShadowBuffer : register (b8)
{
	matrix sWorld;
	matrix lightViewMatrix;
	matrix lightProjectionMatrix;
	float3 sLightPos;
}

// Meshes
Texture2D txt : register(t0); // base
Texture2D txt2 : register(t1); // bump
Texture2D parallaxmap : register(t12); // parallax
Texture2D txt3 : register(t2); // opacity
TextureCube env : register (t11);

//Terrain
Texture2D terrChannel1 : register(t3);
Texture2D terrChannel2 : register(t4);
Texture2D terrChannel3 : register(t5);
Texture2D terrChannel4 : register(t6);

Texture2D gBuffer_Normals : register(t7);
Texture2D gBuffer_Position : register(t8);
Texture2D gBuffer_Random : register(t9);
Texture2D gBuffer_Shadows : register(t10);

sampler shadowss = 
sampler_state
{
    Texture = <gBuffer_Shadows>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};

SamplerState ss;

sampler NormalMapSampler = sampler_state
{
   Texture = <txt2>;
   MinFilter = Linear;
   MagFilter = Linear;
   MipFilter = Linear;   
   AddressU  = Clamp;
   AddressV  = Clamp;
};

struct VOut
{
    float4 worldPos : POSITION; 
	float3 normal : NORMAL; 
	float3 Specnormal : NORMAL2; 
    float2 texcoord : TEXCOORD; 
	float4 tangent : TANGENT; 
    float4 position : SV_POSITION;
    float3 WorldNormal : TEXCOORD2;
	float Depth : FOG;
    float4 color : COLOR;
    float3 View : VIEWD;
	float4 lightViewPosition : TEXCOORD5;
	float3 L : TEXCOORD6;
	float3 N : TEXCOORD7;
	float3 lightPos : TEXCOORD8;
	float3 toeyetangent : EYET;
};

VOut VShader(float4 position : POSITION, float4 normal : NORMAL, float2 texcoord : TEXCOORD, float4 tangent : TANGENT, float3 instancePosition : INSTANCEPOS)
{
    VOut output;

	if (_wind == 1)
	{
		position.x += sin(time+position.x*10);
		position.y += cos(time+position.y*10);
		position.z += sin(time+position.y*10);

		// Parameters controlling the wind effect.
		float3 WindDirection = float3(1, 0, 0);
		float WindWaveSize = 0.1;
		float WindRandomness = 1;
		float WindSpeed = _windspeed;
		float WindAmount = _windamount;

		// Work out how this vertex should be affected by the wind effect.
		float waveOffset = dot(position, WindDirection) * WindWaveSize;
    
		waveOffset += 2 * WindRandomness;
    
		// Wind makes things wave back and forth in a sine wave pattern.
		float wind = sin(time * WindSpeed + waveOffset) * WindAmount;
    
		// But it should only affect the top two vertices of the billboard!
		wind *= (1 - texcoord.y);
    
		position += float4(WindDirection * wind, position.w);
	}

	if (_instance == 1)
	{
		position.x += instancePosition.x;
		position.y += instancePosition.y;
		position.z += instancePosition.z;
	}

	position.w = 1.0f;

	// Calculate the position of the vertex against the world, view, and projection matrices.
    output.position = mul(position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);

    output.worldPos = mul(position, worldMatrix);
	output.WorldNormal = normalize(mul(normal, worldMatrix));

	output.color = float4(0,0,0,1);

    // set the ambient light
	if (_diffuse == 1)
	{
		output.color += ambientcol;
	}

	if (_texture == 1)
	{
		output.texcoord = texcoord;
	}

	// Stuff for dir and bump
	if (_directionall == 1)
	{
		float4 norm = normalize(mul(rotation, normal));
		float diffusebrightness = saturate(dot(norm, lightvec));
		output.color += lightcol * diffusebrightness;
	}

	if (_bumpmap == 1)
	{
		output.normal = normal;
		output.tangent = tangent;
	}

	float3 Cnormal = normalize(mul(normal, worldMatrix));
	output.Specnormal = Cnormal;
	output.View = normalize(eyepos - output.worldPos);

	// SHADOW
    
	if (_shadows == 1)
	{
		// Calculate the position of the vertice as viewed by the light source.
		output.lightViewPosition = mul(position, worldMatrix);
		output.lightViewPosition = mul(output.lightViewPosition, lightViewMatrix);
		output.lightViewPosition = mul(output.lightViewPosition, lightProjectionMatrix);
    
		// Calculate the normal vector against the world matrix only.
		output.normal = mul(normal, (float3x3)worldMatrix);
	
		// Normalize the normal vector.
		output.normal = normalize(output.normal);

		// Calculate the position of the vertex in the world.
		float4 worldPosition = mul(position, worldMatrix);

		// Determine the light position based on the position of the light and the position of the vertex in the world.
		output.lightPos = sLightPos - worldPosition.xyz;

		// Normalize the light position vector.
		output.lightPos = normalize(output.lightPos);
	}
	// END

	if (_parallaxMapping == 1)
	{
		float binormal = cross(tangent,normal);

		float3x3 tbnmatrix = transpose(float3x3(tangent,binormal,normal));
		output.toeyetangent = mul((eyepos - output.position),tbnmatrix);
	}

	// Final stuff
	output.Depth = output.position.z;
	output.color.a = 1;

    return output;
}

// SSAO Operations
float3 getPosition(in float2 uv)
{
	return gBuffer_Position.Sample(ss, uv).xyz;
}

float3 getNormal(in float2 uv)
{
	return normalize(gBuffer_Normals.Sample(ss, uv).xyz * 2.0f - 1.0f);
}

float2 getRandom(in float2 uv)
{
	return normalize(gBuffer_Random.Sample(ss, float2(800,600) * uv / float2(64,64)).xy * 2.0f - 1.0f);
}

float doAmbientOcclusion(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm)
{
	float3 diff = getPosition(tcoord + uv) - p;
	const float3 v = normalize(diff);
	const float d = length(diff)*2; //scale
	return max(0.0,dot(cnorm,v)-5)*(1.0/(1.0+d))*1; // Intensity
}

float4 PShader(VOut input) : SV_TARGET
{
	// If it's wireframe, skip everything else
	if (_wireframe == 1)
	{
		return float4(1,1,1,1); // we may have to do something with this, not sure yet...
	}

	float4 color = input.color;

	float4 spec = float4(0,0,0, 1);
	float4 tex = float4(0,0,0, 1);
	float4 bump = float4(0,0,0, 1);

	input.texcoord.xy *= texture_repeat;
	float2 texCoord = input.texcoord;

	if (_texture == 1)
	{
		if (_parallaxMapping == 1)
		{
			float height = parallaxmap.Sample(ss, texCoord);

			float3 toeyetangent = normalize(input.toeyetangent);
			float2 offset = toeyetangent.xy*height*0.04f;

			texCoord += offset;
		}

		if (_pixelDisorter == 1)
		{
			// Distortion factor
			float NoiseX = _disorterAmount * (time/1000) * sin(input.texcoord.x * input.texcoord.y+time/1000);
			NoiseX=fmod(NoiseX,8) * fmod(NoiseX,4); 

			// Use our distortion factor to compute how much it will affect each
			// texture coordinate
			float DistortX = fmod(NoiseX,5);
			float DistortY = fmod(NoiseX,5+0.002);
 
			// Create our new texture coordinate based on our distortion factor
			texCoord = float2(DistortX,DistortY);
		}

		if (!_bumpmap)
		{
			float4 txtColor = txt.Sample(ss, texCoord);
			color *= txtColor;
		}
		else if (_bumpmap == 1)
		{
			float4 Color = txt.Sample(ss, texCoord);
			float3 N = (2.0 * (txt2.Sample(ss, texCoord))) - 1.0;
			// diffuse
			float D = saturate(dot(N, lightvec));
			// reflection
		    float3 R = normalize(2 * D * N - lightvec);
			// specular
			float S = pow(saturate(dot(R, texCoord*input.tangent)), 2);
			const float4 Ambient = float4(0.3, 0.3, 0.3, 1.0);

			color *= Color*Ambient + Color * D + Color*S; 
		}

		if (_doClip == 1)
		{
			if (_invDoClip == 1)
			{
				color.a = 1.0f - saturate(txt.Sample(ss, texCoord));
			}
			else 
			{
				color.a = saturate(txt.Sample(ss, texCoord));
			}
		}
	}

	if (_specular == 1)
	{
		float4 normal = float4(input.Specnormal, 1.0);
		float4 diffuse = saturate(dot(-lightvec,normal));
		float4 reflect = normalize(2*diffuse*normal-lightvec);
		float4 specular = pow(saturate(dot(reflect,input.View)),15);
 
		color += float4(1,1,1,1)*specular;
	}

	if (_opacityEnable == 1)
	{
		float4 opacity = saturate(txt3.Sample(ss, texCoord));

		color.a *= opacity;
	}

	if (_terrain == 1)
	{
		float4 grassColor;
		float4 slopeColor;
		float4 rockColor;
		float slope;
		float blendAmount;
		float4 textureColor;

		grassColor = terrChannel1.Sample(ss, input.texcoord);

		slopeColor = terrChannel2.Sample(ss, input.texcoord);

		rockColor  = terrChannel3.Sample(ss, input.texcoord);

		// Calculate the slope of this point.
		slope = 1.0f - input.normal.y;

		// Determine which texture to use based on height.
		if(slope < 0.2)
		{
			blendAmount = slope / 0.2;
			textureColor = lerp(grassColor, slopeColor, blendAmount);
		}
	
		if((slope < 0.7) && (slope >= 0.2))
		{
			blendAmount = (slope - 0.2) * (1.0f / (0.7 - 0.2));
			textureColor = lerp(slopeColor, rockColor, blendAmount);
		}

		if(slope >= 0.7) 
		{
			textureColor = rockColor;
		}

		color *= textureColor;
	}

	if (_particle == 1)
	{
		float range = particle_lifeTime-particle_fallOff;

		if (particle_pastTime > range)
		{
			color.a *= saturate(1.0f - (1.0f/( (particle_lifeTime-(particle_lifeTime - particle_fallOff)) / (particle_pastTime-(particle_lifeTime - particle_fallOff)) )));
			
			color.a -= 1.0f/(distance(eyepos.xyz, input.Depth)) / 50.0f;
		}
	}

	if (_shadows == 1)
	{
		float bias;
		float2 projectTexCoord;
		float depthValue;
		float lightDepthValue;
		float lightIntensity;
		float4 textureColor;

		// Set the bias value for fixing the floating point precision issues.
		bias = 0.0001f;

		// Calculate the projected texture coordinates.
		projectTexCoord.x =  input.lightViewPosition.x / input.lightViewPosition.w / 2.0f + 0.5f;
		projectTexCoord.y = -input.lightViewPosition.y / input.lightViewPosition.w / 2.0f + 0.5f;

		// Determine if the projected coordinates are in the 0 to 1 range.  If so then this pixel is in the view of the light.
		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 = gBuffer_Shadows.Sample(ss, projectTexCoord).r;

			// Calculate the depth of the light.
			lightDepthValue = input.lightViewPosition.z / input.lightViewPosition.w;

			// Subtract the bias from the lightDepthValue.
			lightDepthValue = lightDepthValue - bias;

			// 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.
				lightIntensity = saturate(dot(input.normal, -lightvec));

				if(lightIntensity > 0.0f)
				{
					// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
					color += (ambientcol * lightIntensity) * color.a;

					// Saturate the final light color.
					color = saturate(color);
				}
			}
			else
			    color.rgb *= 0.3f;
		}
	}

	if (_fog == 1)
	{
		float l = saturate((input.Depth - FogStart) / (FogEnd - FogStart));
		color = float4(lerp(color,FogColor, l), color.a);
	}

	if (_selected == 1)
	{
		color += float4(0, 0, 0.2, 0);
	}

	color.a = saturate(color.a);

    return color;
}

 

 

Thanks! biggrin.png

Share this post


Link to post
Share on other sites
Advertisement

Can anybody tell me if the parallax mapping is right? huh.png

 

I'm not asking for a fix, I'm just asking for a check, and if you have the time and you want to, you can give me an advice if you want to. happy.png

Share this post


Link to post
Share on other sites

Looks like a bug I had a long while ago when first playing around with parallax mapping. The blurry strifes aren't supposed to be there, really. Just run the DX9 sdk-parallax mapping sample, and compare yours to this. I can't tell you what the exact problem was for me back then and how I solved it, but just study the directx sdk examples (there is actually also one for DX11, I believe)...

Share this post


Link to post
Share on other sites

Pic, please confirm:

 

Nope, still wrong. Unless you want a nightmarish game where everything is blurred and shapes are washed up, thats far, far from looking as it should. Its probably just a small space-convertion issue of some kind. Here is how it is supposed to look like.

 

 

 

 

 

Share this post


Link to post
Share on other sites

It looks definately better than on the sphere, but still, compared to this (see attachment) it looks washed up. Would need to see it in motion to see it more clearl, though.

Share this post


Link to post
Share on other sites

Are you sure that this isn't how it's supposed to look?

Are you asking a question, or telling us that your implementation is correct?  It isn't clear to me (or anyone else I think) what your motive is for writing this thread.  If there is something about your results that seem wrong to you, then please ask about it.  If this is a thread for you to demonstrate your implementation, then put that in the description.

 

So far, I haven't seen you really ask any substantive questions - please help us to understand what you want.

Share this post


Link to post
Share on other sites

Are you asking a question, or telling us that your implementation is correct?

 

Then what about this? huh.png

 

No, thats still not a valid question :S

Share this post


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

  • Advertisement
  • Advertisement
  • Popular Now

  • Advertisement
  • Similar Content

    • By AxeGuywithanAxe
      I wanted to see how others are currently handling descriptor heap updates and management.
      I've read a few articles and there tends to be three major strategies :
      1 ) You split up descriptor heaps per shader stage ( i.e one for vertex shader , pixel , hull, etc)
      2) You have one descriptor heap for an entire pipeline
      3) You split up descriptor heaps for update each update frequency (i.e EResourceSet_PerInstance , EResourceSet_PerPass , EResourceSet_PerMaterial, etc)
      The benefits of the first two approaches is that it makes it easier to port current code, and descriptor / resource descriptor management and updating tends to be easier to manage, but it seems to be not as efficient.
      The benefits of the third approach seems to be that it's the most efficient because you only manage and update objects when they change.
    • By evelyn4you
      hi,
      until now i use typical vertexshader approach for skinning with a Constantbuffer containing the transform matrix for the bones and an the vertexbuffer containing bone index and bone weight.
      Now i have implemented realtime environment  probe cubemaping so i have to render my scene from many point of views and the time for skinning takes too long because it is recalculated for every side of the cubemap.
      For Info i am working on Win7 an therefore use one Shadermodel 5.0 not 5.x that have more options, or is there a way to use 5.x in Win 7
      My Graphic Card is Directx 12 compatible NVidia GTX 960
      the member turanszkij has posted a good for me understandable compute shader. ( for Info: in his engine he uses an optimized version of it )
      https://turanszkij.wordpress.com/2017/09/09/skinning-in-compute-shader/
      Now my questions
       is it possible to feed the compute shader with my orignial vertexbuffer or do i have to copy it in several ByteAdressBuffers as implemented in the following code ?
        the same question is about the constant buffer of the matrixes
       my more urgent question is how do i feed my normal pipeline with the result of the compute Shader which are 2 RWByteAddressBuffers that contain position an normal
      for example i could use 2 vertexbuffer bindings
      1 containing only the uv coordinates
      2.containing position and normal
      How do i copy from the RWByteAddressBuffers to the vertexbuffer ?
       
      (Code from turanszkij )
      Here is my shader implementation for skinning a mesh in a compute shader:
      1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 struct Bone { float4x4 pose; }; StructuredBuffer<Bone> boneBuffer;   ByteAddressBuffer vertexBuffer_POS; // T-Pose pos ByteAddressBuffer vertexBuffer_NOR; // T-Pose normal ByteAddressBuffer vertexBuffer_WEI; // bone weights ByteAddressBuffer vertexBuffer_BON; // bone indices   RWByteAddressBuffer streamoutBuffer_POS; // skinned pos RWByteAddressBuffer streamoutBuffer_NOR; // skinned normal RWByteAddressBuffer streamoutBuffer_PRE; // previous frame skinned pos   inline void Skinning(inout float4 pos, inout float4 nor, in float4 inBon, in float4 inWei) {  float4 p = 0, pp = 0;  float3 n = 0;  float4x4 m;  float3x3 m3;  float weisum = 0;   // force loop to reduce register pressure  // though this way we can not interleave TEX - ALU operations  [loop]  for (uint i = 0; ((i &lt; 4) &amp;&amp; (weisum&lt;1.0f)); ++i)  {  m = boneBuffer[(uint)inBon].pose;  m3 = (float3x3)m;   p += mul(float4(pos.xyz, 1), m)*inWei;  n += mul(nor.xyz, m3)*inWei;   weisum += inWei;  }   bool w = any(inWei);  pos.xyz = w ? p.xyz : pos.xyz;  nor.xyz = w ? n : nor.xyz; }   [numthreads(1024, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID ) {  const uint fetchAddress = DTid.x * 16; // stride is 16 bytes for each vertex buffer now...   uint4 pos_u = vertexBuffer_POS.Load4(fetchAddress);  uint4 nor_u = vertexBuffer_NOR.Load4(fetchAddress);  uint4 wei_u = vertexBuffer_WEI.Load4(fetchAddress);  uint4 bon_u = vertexBuffer_BON.Load4(fetchAddress);   float4 pos = asfloat(pos_u);  float4 nor = asfloat(nor_u);  float4 wei = asfloat(wei_u);  float4 bon = asfloat(bon_u);   Skinning(pos, nor, bon, wei);   pos_u = asuint(pos);  nor_u = asuint(nor);   // copy prev frame current pos to current frame prev pos streamoutBuffer_PRE.Store4(fetchAddress, streamoutBuffer_POS.Load4(fetchAddress)); // write out skinned props:  streamoutBuffer_POS.Store4(fetchAddress, pos_u);  streamoutBuffer_NOR.Store4(fetchAddress, nor_u); }  
    • By mister345
      Hi, can someone please explain why this is giving an assertion EyePosition!=0 exception?
       
      _lightBufferVS->viewMatrix = DirectX::XMMatrixLookAtLH(XMLoadFloat3(&_lightBufferVS->position), XMLoadFloat3(&_lookAt), XMLoadFloat3(&up));
      It looks like DirectX doesnt want the 2nd parameter to be a zero vector in the assertion, but I passed in a zero vector with this exact same code in another program and it ran just fine. (Here is the version of the code that worked - note XMLoadFloat3(&m_lookAt) parameter value is (0,0,0) at runtime - I debugged it - but it throws no exceptions.
          m_viewMatrix = DirectX::XMMatrixLookAtLH(XMLoadFloat3(&m_position), XMLoadFloat3(&m_lookAt), XMLoadFloat3(&up)); Here is the repo for the broken code (See LightClass) https://github.com/mister51213/DirectX11Engine/blob/master/DirectX11Engine/LightClass.cpp
      and here is the repo with the alternative version of the code that is working with a value of (0,0,0) for the second parameter.
      https://github.com/mister51213/DX11Port_SoftShadows/blob/master/Engine/lightclass.cpp
    • 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
       
  • Advertisement