Issue passing a texture to shader.

Started by
14 comments, last by MJP 15 years, 2 months ago
I tried posting this on the XNA community forums but I got no such luck, just questions as to what I'm attempting. Now, my problem here is that I'm creating a texture, filling it full of noise. Then passing it to the shader as a parameter, however the shader won't sample the texture and return anything but black. I know the texture has been set correctly, I've read back the data and it's the same I put in it. But that doesn't explain why when I pass it the shader acts as if it wasn't set and just returns nothing...

private void UpdateSurfaceNoiseGPU(GameTime gameTime)
		{
			if (generateList.Count > 0 == false)
				return;

			// device
			GraphicsDevice device = game.GraphicsDevice;
			Vector2 viewportSize = new Vector2(blockWidth, blockHeight);
			
			// noise info
			Vector4 noiseInfo = generateList[0];
			int noiseWidth = (int)noiseInfo.X;
			int noiseHeight = (int)noiseInfo.Y;

			Texture2D noiseTexture = new Texture2D(device, noiseWidth, noiseHeight, 0, TextureUsage.None, SurfaceFormat.Color);
			Color[] noiseData = new Color[noiseWidth * noiseHeight];

			for(int x = 0; x < noiseWidth; x++)
			{
				for(int y = 0; y < noiseHeight; y++)
				{
					noiseData[x + y * noiseWidth] = new Color( (byte)(Util.ImageProcessing.PerlinNoise.Noise2D(x, y) * 255), 0, 0, 0 );
				}
			}

			noiseTexture.SetData<Color>(noiseData);

			// setup shader constants
			shaderGenerate.Parameters["ViewportSize"].SetValue(viewportSize);
			shaderGenerate.Parameters["Noise"].SetValue(noiseTexture);

			// draw on to surface
			for (UInt32 x = 0; x < mapBlockWidthNum; x++)
			{
				for (UInt32 y = 0; y < mapBlockHeightNum; y++)
				{
					TerrainBlock block = mapBlocks[x, y];
						// set the render target, draw and swap
					device.SetRenderTarget(0, mapSwapBlock);

					batch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
					shaderGenerate.Begin();
					shaderGenerate.CurrentTechnique.Passes[0].Begin();

					batch.Draw(block.SurfaceTexture, Vector2.Zero, Color.White);

					shaderGenerate.CurrentTechnique.Passes[0].End();
					shaderGenerate.End();
					batch.End();

					device.SetRenderTarget(0, null);

					FlipBlockBackBuffer(block);
				}
			}
			generateList.Clear();
			noiseTexture.Dispose();
			device.SetRenderTarget(0, null);
}
The problem exists here: shaderGenerate.Parameters["Noise"].SetValue(noiseTexture); The shader I'm using is a modified version of the SpriteBatch shader, which works in my other process for terrain painting but not this.

// SpriteBatch default variables

texture	Noise;
sampler	NoiseSampler = sampler_state { Texture = <Noise>; MipFilter = Linear; MinFilter = Linear; MagFilter = Linear; AddressU = Clamp; AddressV  = Clamp; };

// spritebatch vertex code...

void SpritePixelShader(inout float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
	float4 surface	= tex2D(TextureSampler, texCoord);
	float4 noise	= tex2D(NoiseSampler, texCoord);
	color = noise;
}
Now, I must be doing something completely wrong. Because my other method works and is similar, I can even pass in textures to the shader. I'm not sure where to approach this because this texture needs to be a shader parameter and not passed in with the Draw call..
Advertisement
Does everything still work correctly if you have your pixel shader sample from TextureSampler? What happens if you send your noise texture as the parameter for SpriteBatch.Draw and sample from TextureSampler, does it still show up? This will verify that you're filling up your noise texture with correct values. You could also verify by running your app in PIX, finding your texture, and viewing it.

If you've made sure your noise texture is coming out directly, you'll want to do some further debugging. Run your app with the debug runtimes active, and make sure you're not getting any errors. Then after that I would have a look at the native D3D9 calls that are happening behind the scene. Find the call to IDirect3DDevice9::SetTexture that's setting your noise texture to the device. Then look through the calls leading up to the actual DrawPrimitive call, and make sure your Effect is being applied properly and that the SpriteBatch is messing up anything for you.
Listen, I know this is not much. But this shouldn't happen. I've tried two different methods. I've tried creating this texture and I've tried using an existing rendertarget and drawing something on it through a pixel shader. Both methods seem to yield no results...

Now, here is the thing that really drives me mad. If I load a texture I've created outside of runtime it works fine.

Without Terrain Generation: (Red: 1, Blue: 0, Green: 0, Height: ~0)
Free Image Hosting at www.ImageShack.us

Terrain With Generation: (Red: 0, Blue: 0, Green: 0, Height: 0) <- this should have a red layer of noise.
// note this uses the noise texture which I have checked that was created perfectly.........
Free Image Hosting at www.ImageShack.us

Terrain with Generation: (Red: tex.r, Blue: tex.b, Green: tex.g, Height: tex.a)
// note this uses a texture of the same size that I pass using the same parameter as the noise, no other code has been changed
Free Image Hosting at www.ImageShack.us

What the heck is going wrong with this application? It can't be the shader, it draws the parameter fine. It can't be the shader object because it passes the texture fine. And it can't be the texture because I've read back the data. It's set perfectly, and even uses base 2.

Please, anything will help...


EDIT:
Quote:Original post by MJP
Does everything still work correctly if you have your pixel shader sample from TextureSampler? What happens if you send your noise texture as the parameter for SpriteBatch.Draw and sample from TextureSampler, does it still show up? This will verify that you're filling up your noise texture with correct values. You could also verify by running your app in PIX, finding your texture, and viewing it.

If you've made sure your noise texture is coming out directly, you'll want to do some further debugging. Run your app with the debug runtimes active, and make sure you're not getting any errors. Then after that I would have a look at the native D3D9 calls that are happening behind the scene. Find the call to IDirect3DDevice9::SetTexture that's setting your noise texture to the device. Then look through the calls leading up to the actual DrawPrimitive call, and make sure your Effect is being applied properly and that the SpriteBatch is messing up anything for you.


Thanks for your response. Yes, it does work fine. However, what I'm planning on attempting is not just simple terrain generation. I also want to have the ability to generate noise on the terrain by a factor of x. Which would give me the ability to create rippes in the terrain.

As for the second part. I've been trying to use PIX to debug it, but it has been no help. The first thing I would have suspected is as you say. However, I'm able to pass a texture loaded by the content manager in. Which boggles my mind...
Can you check to see which resource pool your noise texture was created in? I would think XNA would place in D3DPOOL_MANAGED, but if it wasn't it could cause problems.
I can check that in D3D, but how would I do something like that in XNA? PIX isn't really the most readable output...
Easiest way to do it would be to just make it a size that you'll easily recognize. Then in the PIX resources view just filter by that size, and your texture and surface will show up right away. Then you can view the texture to make sure it has the right data, and view the resource pool and other properties.
In PIX when you take a frame capture does this capture the frame from the begining of when the backbuffer is drawn to when it is flipped?
Yes, it gets everything from the last time you called Present
To answer your question XNA did place this texture in the Managed pool.
Hmm...I'm out of ideas then I'm afraid. When I get home from work I'll see if I can use your app code and shader code to reproduce this behavior.

This topic is closed to new replies.

Advertisement