SkyBox Depth Troubles

Started by
4 comments, last by Hodgman 6 years, 8 months ago

I am having an issue with always passing the depth test and writing my skybox over existing geometry.  I draw the skybox last, and it is all you see on screen.  Turn it off, and my scene renders fine.  Here is the setup, hope someone can tell me where I am wrong (pulling hair out) trying to setup DirectX 12 depth testing correctly:

Header:

using the Microsoft mini engine core as my renderer


	SamplerDescriptor				m_skySampler;
	RootSignature					m_skyRootSig;
	GraphicsPSO					m_skyPSO;

Initialization:


	// root signature sky map
	m_skyRootSig.Reset(2, 2); 
	m_skyRootSig.InitStaticSampler(0, SamplerAnisoWrapDesc, D3D12_SHADER_VISIBILITY_PIXEL); 

	SamplerDesc samplerSkyDesc;
	samplerSkyDesc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
	samplerSkyDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
	samplerSkyDesc.SetTextureAddressMode(D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
	m_skySampler.Create(samplerSkyDesc);

	m_skyRootSig.InitStaticSampler(1, samplerSkyDesc, D3D12_SHADER_VISIBILITY_PIXEL);
	// parameters
	m_skyRootSig[0].InitAsConstantBuffer(0, D3D12_SHADER_VISIBILITY_VERTEX); // vertex shader float4x4 modelToProjection;
	m_skyRootSig[1].InitAsDescriptorRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 6, D3D12_SHADER_VISIBILITY_PIXEL);
	m_skyRootSig.Finalize(L"SkyMap", D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

	/* Sky box PSO */
	m_skyPSO.SetRootSignature(m_skyRootSig);
	
	D3D12_RASTERIZER_DESC rastDesc;
	rastDesc.FillMode = D3D12_FILL_MODE_SOLID;
	rastDesc.CullMode = D3D12_CULL_MODE_NONE; 
	rastDesc.DepthClipEnable = TRUE;
	m_skyPSO.SetRasterizerState(rastDesc);
	
	m_skyPSO.SetBlendState(BlendDisable);

	D3D12_DEPTH_STENCILOP_DESC stencilOp;
	stencilOp.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	stencilOp.StencilPassOp = D3D12_STENCIL_OP_KEEP;
	stencilOp.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	stencilOp.StencilFunc = D3D12_COMPARISON_FUNC_NEVER;

	D3D12_DEPTH_STENCIL_DESC skyBox = DepthStateDisabled;
	skyBox.DepthEnable = TRUE; // depth testing enabled on pixel shader
	skyBox.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; // read only
	skyBox.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; 
	skyBox.StencilEnable = FALSE;
	skyBox.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
	skyBox.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
	skyBox.FrontFace = stencilOp;
	skyBox.BackFace = stencilOp;

	m_skyPSO.SetDepthStencilState(skyBox);	

	D3D12_INPUT_ELEMENT_DESC vertElemSky[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
	};
	
	m_skyPSO.SetInputLayout(_countof(vertElemSky), vertElemSky);
		
	m_skyPSO.SetSampleMask(0xFFFFFFFF);
	m_skyPSO.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
	m_skyPSO.SetRenderTargetFormats(1, &ColorFormat, DepthFormat);
	m_skyPSO.SetVertexShader(g_pSkyVS, sizeof(g_pSkyVS));
	m_skyPSO.SetPixelShader(g_pSkyPS, sizeof(g_pSkyPS));
	m_skyPSO.Finalize();

Shader parameter validation:


#define Sky_RootSig \
	"RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT), " \
	"CBV(b0, visibility = SHADER_VISIBILITY_VERTEX), " \
	"DescriptorTable(SRV(t0, numDescriptors = 6), visibility = SHADER_VISIBILITY_PIXEL)," \
	"StaticSampler(s0, maxAnisotropy = 8, visibility = SHADER_VISIBILITY_PIXEL)," \
	"StaticSampler(s1, visibility = SHADER_VISIBILITY_PIXEL," \
		"addressU = TEXTURE_ADDRESS_WRAP," \
		"addressV = TEXTURE_ADDRESS_WRAP," \
		"addressW = TEXTURE_ADDRESS_WRAP," \
		"comparisonFunc = COMPARISON_LESS_EQUAL," \
		"filter = FILTER_MIN_MAG_LINEAR_MIP_POINT)"

Vertex Shader:


#include "SkyRS.hlsli"

cbuffer VSConstants : register(b0)
{
	float4x4 modelToProjection;
};

struct VSInput
{
	float3 position : POSITION;
};

struct VSOutput
{
	float4 position  : SV_POSITION; // screen space position of the pixel
	float3 texLookUp : POSITION;
};

[RootSignature(Sky_RootSig)]
VSOutput main(VSInput vsInput)
{
	VSOutput vsOutput;

	vsOutput.texLookUp = vsInput.position; // use position as index to cube map
	
	float3 projected = mul(vsInput.position, (float3x3)modelToProjection); // ignore translation 
	vsOutput.position = float4(projected, 1.0f).xyww; // Set z = w which forces the farthest possible z value
	//vsOutput.position = float4(vsInput.position, 1.0f).xyww;

	return vsOutput;
}

Pixel shader:



#include "SkyRS.hlsli"

struct VSOutput
{
	float4 position  : SV_POSITION; // screenspace position of the pixel
	float3 texLookUp : POSITION;
};

TextureCube cubeMap : register(t0);

SamplerState sampler0 : register(s0);
SamplerState sampler1 : register(s1);

[RootSignature(Sky_RootSig)]
float3 main(VSOutput pin) : SV_Target0
{ 
	//return float3(1.f,0.f,0.f);
	return cubeMap.Sample(sampler1, pin.texLookUp);
}

 

Advertisement

I don't know the correct way to do it in DirectX, but in OpenGL I draw the skybox last with the depth range set to [1.0,1.0] instead of [0.0,1.0], with depth test less-than-or-equal. This way, all output fragments of the skybox are at the furthest depth value (the clear depth), and won't overdraw any foreground objects.

The sky box is behind everything, so you can just do so at the end of the vertexshader :

 


return float4(vsOutput.xy,1,1);

 

This way the geometry is pushed to the far plane or infinity, independently from his scale. A change to the viewport bounds is not the best option as it is an heavier state to change + in theory, you should not set a NIL range for depth.

@galop1n Thank you, but my shader is doing the same thing with vsOutput.position = float4(projected, 1.0f).xyww; so that does not appear to be the issue.

@Aressera Thank you, thank you!  Solved!  This is a great practice I will now adopt.  By setting the depth range to one value, it can be only one value.  However, my problem still exists, so that means only one thing: the comparison function is not working. 

Solution:  Mini-engine open source code from Microsoft optimizes the z-pass by reversing it (0 far plane and 1.0 near plane).  So the comparison tests have to be greater-than-or-equal rather than less-than-or-equal.  I changed that and it works perfectly now.

Thank you both for your help.  First time posting here because I was out of ideas and you led me to water :)

 

Quote

samplerSkyDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;

Sampler comparisons are used for shadow-mapping, where you want your texture filter to compare the texel values against a reference that you provide and return a boolean result (0.0f or 1.0f).

This topic is closed to new replies.

Advertisement