Jump to content

  • Log In with Google      Sign In   
  • Create Account

Transparent geometry in deferred shading (help)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 06 July 2012 - 11:55 AM

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:

http://cl.ly/1o423J311Y302b1w3P22

(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, 06 July 2012 - 12:01 PM.


Sponsor:

#2 MJP   Moderators   -  Reputation: 11352

Like
0Likes
Like

Posted 06 July 2012 - 12:00 PM

You definitely don't want to clear the depth buffer, you need that for depth testing your transparent geo.

#3 Koehler   Members   -  Reputation: 228

Like
0Likes
Like

Posted 06 July 2012 - 12:01 PM

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.

#4 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 06 July 2012 - 12:04 PM

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, 06 July 2012 - 12:08 PM.


#5 Ashaman73   Crossbones+   -  Reputation: 7480

Like
0Likes
Like

Posted 06 July 2012 - 12:05 PM

3. Clearing DepthBuffer (otherwise the transparent geometry doesn't appear)

No need to clear the depth buffer when enabling depth tests in 2. Double check your depth test function.

Which basically gives me something like that:

Double check your blending function when rendering your transparent objects, it should be like source_alpha, inv_source_alpha.

Best to post some code.

#6 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 06 July 2012 - 12:17 PM

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, 06 July 2012 - 12:18 PM.


#7 Koehler   Members   -  Reputation: 228

Like
0Likes
Like

Posted 06 July 2012 - 12:53 PM

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."

#8 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 06 July 2012 - 01:21 PM

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, 06 July 2012 - 01:24 PM.


#9 Koehler   Members   -  Reputation: 228

Like
1Likes
Like

Posted 06 July 2012 - 04:08 PM

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).

#10 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 06 July 2012 - 04:17 PM

Thanks, I'll try it out as soon as I have time.

#11 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 07 July 2012 - 05:14 PM

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.

#12 Koehler   Members   -  Reputation: 228

Like
0Likes
Like

Posted 09 July 2012 - 08:56 AM

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?

#13 lipsryme   Members   -  Reputation: 1020

Like
0Likes
Like

Posted 09 July 2012 - 11:17 AM

Alright, so...

This is how I draw my opaque content:
	  //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);
			}

ClearGBuffer:
	    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);
	    }

MakeLightMap:
	  /// <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);
		}

MakeGBuffer:
	
			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);
				}
			}
		}

Compose:

		/// <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);
		}



And here's how I draw transparent geometry
	  /// <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);
		}

Edited by lipsryme, 09 July 2012 - 11:18 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS