Sign in to follow this  
lipsryme

Transparent geometry in deferred shading (help)

Recommended Posts

lipsryme    1522
I'm currently implementing the seperate forward pass after the opaque geometry (deferred) for rendering transparent alpha blended geometry.
Though I feel like I'm still lacking a bit of understanding how to accomplish this.

What I'm doing is this:

1. Render opaque geometry using deferred shading (Gbuffer, LightPass, Compose)
2. Sorting Transparent geometry back to front and setting DepthEnabled and DepthWrite off.
3. Clearing DepthBuffer (otherwise the transparent geometry doesn't appear)
4. Render transparent geometry.

Now the problem that arises is that the transparent render pass has no information about the opaque objects being there. Which basically gives me something like that:

[url="http://cl.ly/1o423J311Y302b1w3P22"]http://cl.ly/1o423J311Y302b1w3P22[/url]

(purple colored object would be the transparent one)

Am I wrong with my assumption ? And how would I go about that problem ? Edited by lipsryme

Share this post


Link to post
Share on other sites
Koehler    228
You need to re-use your depth buffer from deferred shading. This means that when doing your forward pass you must render to a target consisting of your composed color and your original G-buffer's depth buffer. This way your forward-lit geometry ends up in the composed image, and will properly interact with your opaque geometry in terms of depth.

Share this post


Link to post
Share on other sites
lipsryme    1522
Hmm both are being rendered to the back buffer at the moment. I just tried it without clearing the depth buffer, oddly enough it still gives me the same result. Do I not have to set the Depth off or at least read only when doing the transparent geometry ? Edited by lipsryme

Share this post


Link to post
Share on other sites
Ashaman73    13715
[quote name='lipsryme' timestamp='1341597310' post='4956398']
3. Clearing DepthBuffer (otherwise the transparent geometry doesn't appear)
[/quote]
No need to clear the depth buffer when enabling depth tests in 2. Double check your depth test function.

[quote name='lipsryme' timestamp='1341597310' post='4956398']
Which basically gives me something like that:
[/quote]
Double check your blending function when rendering your transparent objects, it should be like source_alpha, inv_source_alpha.

Best to post some code.

Share this post


Link to post
Share on other sites
lipsryme    1522
I use the xna syntax: BlendState.AlphaBlend which uses
AlphaBlendFunction = Add
AlphaDestinationBlend = InverseSourceAlpha
AlphaSourceBlend = One

Same with ColorBlend.

And if I use DepthWrite the transparent object does not draw on the screen.

Any specific code that I could post which would help ? Edited by lipsryme

Share this post


Link to post
Share on other sites
Koehler    228
Is Depth Write on during your lighting or compose steps? If so, that could be your problem. You should not be writing to the depth buffer during either of them, nor should you be testing against the depth buffer in your composition step.

Assuming your camera transforms are the same for your forward and G-Buffer phases, and you haven't destroyed the depth buffer by enabling depth write for any step after creating your G-buffer, enabling depth test for your transparent draw step should "just work."

Share this post


Link to post
Share on other sites
lipsryme    1522
I got it exactly like you said now still the same.
The problem here I think is that XNA discards the depth buffer after switching the rendertarget.
Problem though is when telling it to preserve contents my GBuffer pass doesn't output anything.
(PIX debugging tells me "This pixel was eliminated because: It failed the depth test")

As I see it my only workarounds here are either:

- Drawing Z only geometry again

or

- Outputting Depth from my DepthBuffer (written out in the gbuffer pass)

correct ? Edited by lipsryme

Share this post


Link to post
Share on other sites
Koehler    228
It looks like you can actually hack this, kind of.


as of 4.0, when using multiple render targets, XNA associates the depth buffer with the render target bound to index 0. (I assume you've got a couple, to represent color/depth/normal).

This is just a guess, but I think you can reuse your depth values by doing the following:

1. Pick a render target you DON'T need for image composition. Set it to preserve contents (or I guess you could bind a "dummy" texture that you won't sample here);
2. Bind this render target to index 0, put your color, specular power, whatever other targets you render to on higher indices.
3. Clear color and depth.
4. Do your G-buffer pass.
5. Disable depth writing
5. Unbind your render targets for now.
6. Do your light pass.
7. Disable depth testing, and rebind the render target you had on index 0 for the G-Buffer pass (once again to index 0)
8. Bind the render target for your final deferred shading output to index 1 (or whichever index you choose that is > 0)
9. Do the image composition pass, writing the final color into the render target on index 1.
10. re-enable depth testing. (Writing should stay OFF if you want consistency in the appearance of your translucent objects)
11. Draw your transparent geometry, outputting the lit color value to index 1
12. Disable depth testing again
13. Bind the render target from index 1 as a texture, and copy it to the backbuffer via a full screen draw. This also gives you an opportunity to post-process your rendered image however you see fit.

..Long list, but I think it'd do the trick.

..Alternatively you could write a light prepass renderer, where your "image composition" pass involves re-drawing your opaque geometry anyway, and just associate the correct depths with your composition render target automatically. The only way to know which would be more efficient in XNA would be to try both (or see if somebody has).

Share this post


Link to post
Share on other sites
lipsryme    1522
Alright so I've done it until part 11. and checking the rendertarget but what I get is only the opaque cube not the transparent one. It only appears again if I enable depth write. But when that happens I get the same old result.

Share this post


Link to post
Share on other sites
Koehler    228
I'm out of suggestions based on the description alone. Could you please post the section of code you're using to do these draws?

Share this post


Link to post
Share on other sites
lipsryme    1522
Alright, so...

This is how I draw my opaque content:
[CODE]
//If there's opaque geometry
if (this.renderQueue.Count > 0)
{
// Set States
this.device.BlendState = BlendState.Opaque;
this.device.DepthStencilState = DepthStencilState.Default;
this.device.RasterizerState = RasterizerState.CullCounterClockwise;
// Clear GBuffer
ClearGBuffer();
foreach (Mesh mesh in this.renderQueue)
{
// Make our GBuffer
MakeGBuffer(mesh);
}
if (this.useLighting)
{
// Make LightMap
MakeLightMap();
}
// Compose our final image
ComposeFinalImage(null);
}
[/CODE]

ClearGBuffer:
[CODE]
private void ClearGBuffer()
{
// Set to ReadOnly depth for now
this.device.DepthStencilState = DepthStencilState.DepthRead;
// Set GBuffer Render Targets
this.device.SetRenderTargets(GBufferTargets);
// Set Clear Effect
this.Clear.CurrentTechnique.Passes[0].Apply();
// Draw
this.fsq.Draw(device);
}
[/CODE]

MakeLightMap:
[CODE]
/// <summary>
/// Creates our LightMap
/// </summary>
private void MakeLightMap()
{
// Set LightMap RenderTarget
this.device.SetRenderTarget(LightMap);
// Clear to transparent black
this.device.Clear(ClearOptions.Target, Color.Transparent, 1.0f, 0);
// Set States
this.device.BlendState = LightMapBS;
this.device.DepthStencilState = DepthStencilState.DepthRead;
// GBuffer Sampler 1 (Depth)
this.device.Textures[0] = this.GBufferTargets[0].RenderTarget;
this.device.SamplerStates[0] = SamplerState.PointClamp;
// GBuffer Sampler 2 (Albedo)
this.device.Textures[1] = this.GBufferTargets[1].RenderTarget;
this.device.SamplerStates[1] = SamplerState.PointClamp;
// GBuffer Sampler 3 (Normal)
this.device.Textures[2] = this.GBufferTargets[2].RenderTarget;
this.device.SamplerStates[2] = SamplerState.PointClamp;
// Calculate inverseView
Matrix inverseView = Matrix.Invert(this.engine.GetCamera.View);
// Calculate inverseViewProjection
Matrix inverseViewProjection = Matrix.Invert(this.engine.GetCamera.View * this.engine.GetCamera.Projection);
// Set the Directional Lights geometry buffers
fsq.ReadyBuffers(device);
foreach (DirectionalLightSource light in this.lightManager.GetDirectionalLights)
{
// Set Directional Light parameters
this.DeferredLighting.CurrentTechnique = this.DeferredLighting.Techniques["Directional"];
this.DeferredLighting.Parameters["inverseViewProjection"].SetValue(inverseViewProjection);
this.DeferredLighting.Parameters["inverseView"].SetValue(inverseView);
this.DeferredLighting.Parameters["CameraPosition"].SetValue(this.engine.GetCamera.Position);
this.DeferredLighting.Parameters["GBufferTextureSize"].SetValue(this.GBufferTextureSize);
this.DeferredLighting.Parameters["LightDirection"].SetValue(light.Direction);
this.DeferredLighting.Parameters["LightColor"].SetValue(light.Color);
this.DeferredLighting.Parameters["AmbientColor"].SetValue(this.lightManager.GetAmbientColor());
this.DeferredLighting.Parameters["LightIntensity"].SetValue(light.Intensity);
// Apply
this.DeferredLighting.CurrentTechnique.Passes[0].Apply();
// Draw FullscreenQuad
this.fsq.JustDraw(device);
}
// Set states off
this.device.BlendState = BlendState.Opaque;
this.device.RasterizerState = RasterizerState.CullCounterClockwise;
this.device.DepthStencilState = DepthStencilState.DepthRead;
this.device.SetRenderTarget(null);
}
[/CODE]

MakeGBuffer:
[CODE]

GBuffer.Parameters["View"].SetValue(camera.View);
GBuffer.Parameters["Projection"].SetValue(camera.Projection);
GBuffer.Parameters["farClip"].SetValue(camera.FarClip);
GBuffer.Parameters["materialID"].SetValue((int)material.GetMaterialID);

//Get Transforms
Matrix[] transforms = new Matrix[model.Bones.Count];
model.CopyAbsoluteBoneTransformsTo(transforms);
//Draw Each ModelMesh
foreach (ModelMesh modelmesh in model.Meshes)
{
//Draw Each ModelMeshPart
foreach (ModelMeshPart part in modelmesh.MeshParts)
{
//Set Vertex Buffer
device.SetVertexBuffer(part.VertexBuffer, part.VertexOffset);
//Set Index Buffer
device.Indices = part.IndexBuffer;
WorldMatrix = transforms[modelmesh.ParentBone.Index] * transform;
//Set World
GBuffer.Parameters["World"].SetValue(WorldMatrix);
SetShaderVariables(part, GBuffer, true);

//Set WorldIT
GBuffer.Parameters["WorldViewIT"].SetValue(Matrix.Transpose(Matrix.Invert((transforms[modelmesh.ParentBone.Index] * transform) * camera.View)));
// Apply pass
GBuffer.CurrentTechnique.Passes[0].Apply();

//Draw
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.NumVertices, part.StartIndex, part.PrimitiveCount);
}
}
}
[/CODE]

Compose:

[CODE]
/// <summary>
/// Composes the picture
/// </summary>
private void ComposeFinalImage(RenderTarget2D output)
{
// Set Composition Target
this.device.SetRenderTargets(depthTarget, compositionTarget);

// Clear
//this.device.Clear(ClearOptions.Target, Color.Transparent, 1.0f, 0);
// Set Albedo Buffer
this.device.Textures[0] = this.GBufferTargets[1].RenderTarget;
this.device.SamplerStates[0] = SamplerState.AnisotropicClamp;
// Set LightMap
this.device.Textures[1] = LightMap;
this.device.SamplerStates[1] = SamplerState.PointClamp;
// Set Effect parameters
this.Compose.Parameters["GBufferTextureSize"].SetValue(this.GBufferTextureSize);
// Apply
this.Compose.CurrentTechnique.Passes[0].Apply();
// Draw FullscreenQuad
this.fsq.Draw(device);
}
[/CODE]



And here's how I draw transparent geometry
[CODE]
/// <summary>
/// Renders transparent geometry sorted back-to-front
/// </summary>
private void DrawTransparentGeometry()
{
// Clear DepthStencil Buffer with TransparentBlack
//this.device.Clear(ClearOptions.Stencil, Color.Black, 1.0f, 0);
if (this.transparentMeshRenderQueue.Count > 0)
{
// Set States
//this.device.BlendState = BlendState.AlphaBlend;
this.device.DepthStencilState = DepthStencilState.Default;
this.device.RasterizerState = RasterizerState.CullCounterClockwise;
// Go through each mesh in transparent mesh render queue
for (int i = 0; i < this.transparentMeshRenderQueue.Count; i++)
{
// If there's more than one mesh inside the queue
if (this.transparentMeshRenderQueue.Count > (i + 1))
{
// If an object's depth is in front of the other one's, swap their places
if (this.transparentMeshRenderQueue[i].GetPosition.Z < this.transparentMeshRenderQueue[i + 1].GetPosition.Z)
{
TransparentMesh mesh = this.transparentMeshRenderQueue[i + 1];
this.transparentMeshRenderQueue[i + 1] = this.transparentMeshRenderQueue[i];
this.transparentMeshRenderQueue[i] = mesh;
}
}
}

// Draw Mesh
for (int i = 0; i < this.transparentMeshRenderQueue.Count; i++)
{
if (this.transparentMeshRenderQueue[i].mesh.GetMaterial.useLighting)
{
foreach (DirectionalLightSource light in this.lightManager.GetDirectionalLights)
{
this.transparentMeshRenderQueue[i].mesh.GetMaterial.GetEffect.Parameters["CameraPosition"].SetValue(this.engine.GetCamera.Position);
this.transparentMeshRenderQueue[i].mesh.GetMaterial.GetEffect.Parameters["LightDirection"].SetValue(light.Direction);
this.transparentMeshRenderQueue[i].mesh.GetMaterial.GetEffect.Parameters["LightColor"].SetValue(light.Color);
this.transparentMeshRenderQueue[i].mesh.GetMaterial.GetEffect.Parameters["AmbientColor"].SetValue(this.lightManager.GetAmbientColor());
this.transparentMeshRenderQueue[i].mesh.GetMaterial.GetEffect.Parameters["LightIntensity"].SetValue(light.Intensity);
this.transparentMeshRenderQueue[i].mesh.DrawForward(device, this.engine.GetCamera);
}
}
else
{
this.transparentMeshRenderQueue[i].mesh.DrawForward(device, this.engine.GetCamera);
}
}

}
this.device.SetRenderTarget(null);
}
[/CODE] Edited by lipsryme

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this