Advertisement

Terrain Rendering

Started by March 08, 2018 07:32 AM
4 comments, last by Vilem Otte 6 years, 10 months ago

I added terrain rendering to my sky+lensflare rendering pipeline. I render terrain onto a render-target and bind it to the backbuffer as a texture. I do this in pixel shader of the fullscreen quad.


return txTerrain.Sample(samLinear, Input.TextureCoords)+ txSkyDome.Sample(samLinear, Input.TextureCoords) +  float4(LensFlareHalo * txDirty.Sample(samLinear, Input.TextureCoords).rgb * Intensity, 1);

The problem is, the terrain is blended with sky. How do I fix this?


	pImmediateContext->OMSetDepthStencilState(pDSState_DD, 1); //disable depth buffer

	// render sky dome
	pImmediateContext->OMSetRenderTargets(1, &pSkyDomeRTV, pDepthStencilView);
	pImmediateContext->ClearRenderTargetView(pSkyDomeRTV, Colors::Black);
	pImmediateContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	//creating the sky texture in the cs shader
	 float Theta = .2f;//XM_PI*(float)tElapsed/50;
	 float Phi = XM_PIDIV4;
	 CSCONSTANTBUF cscb;
	 cscb.vSun = XMFLOAT3(cos(Theta)*cos(Phi), sin(Theta), cos(Theta)*sin(Phi));
	 cscb.MieCoeffs = XMFLOAT3((float)MieCoefficient(m, AerosolRadius, 680), (float)MieCoefficient(m, AerosolRadius, 530), (float)MieCoefficient(m, AerosolRadius, 470));
	 cscb.RayleighCoeffs = XMFLOAT3((float)RayleighCoefficient(680), (float)RayleighCoefficient(530), (float)RayleighCoefficient(470));
	 cscb.fHeight = 10;
	 cscb.fWeight = 10;
	 cscb.fWeight2 = 10;
	pImmediateContext->UpdateSubresource( pCSConstBuffer, 0, NULL, &cscb, 0, 0 );
	
	 UINT UAVCounts = 0;
	pImmediateContext->CSSetUnorderedAccessViews(0, 1, &pSkyUAV, &UAVCounts);
	pImmediateContext->CSSetConstantBuffers(0, 1, &pCSConstBuffer);
	pImmediateContext->CSSetShader(pComputeShader, NULL, 0);
	pImmediateContext->Dispatch(8, 8, 1);
	pImmediateContext->CSSetUnorderedAccessViews(0, 1, &NULLUAV, 0);
	pImmediateContext->CSSetShader(NULL, NULL, 0);

	//setting dome relevant vs and ps stuff
	pImmediateContext->IASetInputLayout(pVtxLayout);
	uiStride =  sizeof(VTX);
	pImmediateContext->IASetVertexBuffers(0, 1, &pVtxSkyBuffer, &uiStride,  &uiOffset);
	pImmediateContext->IASetIndexBuffer(pIndxSkyBuffer, DXGI_FORMAT_R32_UINT,  0);
	pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	pImmediateContext->VSSetConstantBuffers(0, 1, &pVSSkyConstBuffer);
	pImmediateContext->VSSetShader(pVtxSkyShader, NULL, 0);
	pImmediateContext->PSSetShader(pPixlSkyShader, NULL, 0);
    pImmediateContext->PSSetShaderResources(0, 1, &pSkySRV);
	pImmediateContext->PSSetSamplers(0, 1, &SampState);
	
	mgWorld = XMMatrixTranslation(MyCAM->GetEye().m128_f32[0], MyCAM->GetEye().m128_f32[1], MyCAM->GetEye().m128_f32[2]);
	//drawing the sky dome
    VSCONSTANTBUF cb;
	cb.mWorld = XMMatrixTranspose( mgWorld );
	cb.mView = XMMatrixTranspose( mgView );
	cb.mProjection = XMMatrixTranspose( mgProjection );
	pImmediateContext->UpdateSubresource( pVSSkyConstBuffer, 0, NULL, &cb, 0, 0 );
	pImmediateContext->DrawIndexed((UINT)MyFBX->myInds.size(),0, 0);
	pImmediateContext->VSSetShader(0, NULL, 0);
	pImmediateContext->PSSetShader(0, NULL, 0);

	pImmediateContext->OMSetDepthStencilState(pDSState_DE, 1); //enable depth buffer

	// terrain rendering
	pImmediateContext->OMSetRenderTargets(1, &pTerRTV, pDepthStencilView);
	pImmediateContext->ClearRenderTargetView(pTerRTV, Colors::Black);
	pImmediateContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	mgWorld = XMMatrixIdentity();// XMMatrixRotationY( (float)tElapsed );
	VSCONSTANTBUF tcb;
	tcb.mWorld = XMMatrixTranspose( mgWorld );
	tcb.mView = XMMatrixTranspose( mgView );
	tcb.mProjection = XMMatrixTranspose( mgProjection );
	pImmediateContext->UpdateSubresource( pVSTerrainConstBuffer, 0, NULL, &tcb, 0, 0 );

	//setting terain relevant vs and ps stuff
	pImmediateContext->IASetInputLayout(pVtxTerrainLayout);
	uiStride =  sizeof(TERVTX);
	pImmediateContext->IASetVertexBuffers(2, 1, &pVtxTerrainBuffer, &uiStride,  &uiOffset);
	pImmediateContext->IASetIndexBuffer(pIndxTerrainBuffer, DXGI_FORMAT_R32_UINT,  0);
	pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	pImmediateContext->VSSetConstantBuffers(0, 1, &pVSTerrainConstBuffer);
	pImmediateContext->VSSetShader(pVtxTerrainShader, NULL, 0);
	pImmediateContext->PSSetShader(pPixlTerrainShader, NULL, 0);
	pImmediateContext->PSSetShaderResources(0, 1, &pTerrainSRV);
	pImmediateContext->PSSetSamplers(0, 1, &TerrainSampState);
	pImmediateContext->DrawIndexed((UINT)MyTerrain->myInds.size(), 0, 0);
	pImmediateContext->VSSetShader(0, NULL, 0);
	pImmediateContext->PSSetShader(0, NULL, 0);

	//downsampling stage for lens flare
	pImmediateContext->OMSetRenderTargets(1, &pSkyDomeBlurRTV, pDepthStencilView);
	pImmediateContext->ClearRenderTargetView(pSkyDomeBlurRTV, Colors::Black);
	pImmediateContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
	
	uiStride =  sizeof(VTX);
	pImmediateContext->IASetInputLayout(pVtxCamLayout);
	pImmediateContext->IASetVertexBuffers(1, 1, &pVtxCamBuffer, &uiStride,  &uiOffset);
	pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
	pImmediateContext->VSSetShader(pVtxSkyBlurShader, NULL, 0);
	pImmediateContext->PSSetShader(pPixlSkyBlurShader, NULL, 0);
	pImmediateContext->PSSetShaderResources(0, 1, &pSkyDomeSRV);//sky+dome texture
	pImmediateContext->PSSetSamplers(0, 1, &CamSampState);
	pImmediateContext->Draw(4, 0);
	pImmediateContext->VSSetShader(0, NULL, 0);
	pImmediateContext->PSSetShader(0, NULL, 0);

	//backbuffer stage where lens flare code and terrain texture are set
	pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, pDepthStencilView);
	pImmediateContext->ClearRenderTargetView(pRenderTargetView, Colors::Black);
	pImmediateContext->ClearDepthStencilView(pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	//	uiStride =  sizeof(VTX);
	//	pImmediateContext->IASetInputLayout(pVtxCamLayout);
	//	pImmediateContext->IASetVertexBuffers(1, 1, &pVtxCamBuffer, &uiStride,  &uiOffset);
	//	pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
	pImmediateContext->VSSetShader(pVtxCamShader, NULL, 0);
	pImmediateContext->PSSetShader(pPixlCamShader, NULL, 0);
	pImmediateContext->PSSetShaderResources(0, 1, &pSRV);// dirty texture
	pImmediateContext->PSSetShaderResources(1, 1, &pSkyDomeBlurSRV);//sky+dome blurred texture
	pImmediateContext->PSSetShaderResources(2, 1, &pSkyDomeSRV);//sky+dome texture
	pImmediateContext->PSSetShaderResources(3, 1, &pTerSRV);//terrain texture
	pImmediateContext->PSSetSamplers(0, 1, &CamSampState);
	pImmediateContext->Draw(4, 0);
	pImmediateContext->VSSetShader(0, NULL, 0);
	pImmediateContext->PSSetShader(0, NULL, 0);

	pSwapChain->Present(0, 0);

 

tst_png_8b08d196f1fe4fccf99e3e6d86700f82.png

I will give a more proper answer later in the evening (I'm on my way to lunch now), but you have one concept wrong - terrain and skydome should both be, in most cases, rendered into same render target.

The lens flare effect we have been talking about in previous thread is a post-processing effect (it should be done mainly on final image your scene rendering outputs). That way also very bright spot in your scene can cast lens flares (imagine situation a sun is shining on a car in your terrain - lens flare can also be created by sun reflection on the car).

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

Advertisement

You've got a point there. But if I use the same render target for both skydome and terrain and snowy texture is used for terrain, downsampling stage(where bright spot is generated) also causes lens flare off the terrain. That's why I separated them.

@Vilem OtteI'm waiting for your answer sir

Alright - to describe the problem you're hitting, what you do in the code:

  1. Clear buffer A & depth
  2. Render sky dome into buffer A
  3. Clear buffer B & depth
  4. Render terrain into buffer B
  5. Clear buffer C & depth
  6. Downsample lens flare into buffer C
  7. Clear backbuffer & depth
  8. Mix lens flare and terrain

Now, I guess you already see few problems, the main one is - how to mix lens flare and terrain. And you simply are unable to do so due to one thing - you don't know where there is terrain and where there is lens flare. You're mixing the color as:

bufferA + bufferB + lensflare(bufferC)

But this is completely wrong! Simply because your sky dome is actually covering most of the screen (if not all of it), and therefore it is also behind the terrain (which makes your terrain look like it was additive blended on the sky).

So, I know I proposed a solution of rendering both - terrain and sky dome into buffer A, but as you noted that is not an option (as your terrain will also behave like lens flare).

There are few ways how you could solve this problem - let me describe some of them:

1) Use one depth buffer for both - terrain and sky dome

And it would work like this:

  1. Clear buffer B & depth
  2. Render terrain into buffer B
  3. Clear buffer A (but do NOT clear depth)
  4. Render sky dome into buffer A
  5. Clear buffer C & depth
  6. Downsample lens flare into buffer C
  7. Clear backbuffer & depth
  8. Mix lens flare and terrain

To make this work properly you need to properly either position your sky dome (to be behind the terrain), or properly set up depth test (to pass only on fragments where terrain was not rendered).

2) Do the depth testing yourself

Which would work like this (and require additional depth buffers)

  1. Clear buffer A & depth A
  2. Render sky dome into buffer A (+ use depth A)
  3. Clear buffer B & depth B
  4. Render terrain into buffer B (+ use depth B)
  5. Clear buffer C & depth
  6. Downsample lens flare into buffer C
  7. Clear backbuffer & depth
  8. Mix lens flare and terrain

And you would mix them as following:


if (depthB == depthB clear value)
    return (bufferA + lensflare(bufferC));
else
    return (bufferB + lensflare(bufferC));

The downside of this method (and the following one) is that lens flare will actually be able to shine through your terrain, unless you would use depth buffers during down-sampling step!

3) Better version than 2) - be clever!

Instead of additional depth buffers, clear buffer A and buffer B alpha to 0, work as in original but the mixing would be:


if (bufferB.w == 0)
    return (bufferA + lensflare(bufferC));
else 
    return (bufferB + lensflare(bufferC));

It has the same downside as 2) of course. Although this time you would just use alpha channel of bufferA instead of depth.

Conclusion

You're basically mixing multiple images together, and you need additional information of when mixing them - that required information is when to use terrain buffer and when to use sky dome buffer. Without this information you can't determine which one should be used for given pixel and the only way to know it is to either use a depth buffer properly (requires your scene(s) which you want to mix to be set up properly), or use some additional value per pixel as a mask (introduces redundancy), or use depth buffers used for previous render targets (introduces redundancy).

 

Here is a reference to presentation about a game where I saw such method for the first time - http://wiki.polycount.com/w/images/d/d5/Making_of_sotc.pdf

Look up in the paper how they mix foreground and background based on a mask. It's quite interesting read (even though the layout of the file is terrible, as it's a translation from Japanese). This is a making-of presentation authors of Shadow of Colossus (the old one on PS2) made. I believe around page 3 there is some explanation.

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

This topic is closed to new replies.

Advertisement