Trying to make a glow effect in xna but it doesn't show the glow or any change at all. Also my back color is purple instead of black and I can't change that either :
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
spriteBatch.Begin();
foreach (Bullet bullet in bulletList)
{
Texture2D bulletTexture = textures[bullet.bulletType];
spriteBatch.Draw(
bulletTexture,
new Rectangle(
(int)bullet.position.X,
(int)bullet.position.Y,
bulletTexture.Width,
bulletTexture.Height
),
null,
Color.White,
MathHelper.ToRadians(bullet.angle),
new Vector2(
bulletTexture.Width / 2,
bulletTexture.Height / 2
),
SpriteEffects.None,
0
);
}
spriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black);
postProcessEffect.CurrentTechnique = postProcessEffect.Techniques["Blur"];
spriteBatch.Begin();
spriteBatch.Draw(
bulletRenderTarget,
new Vector2(0, 0),
Color.White
);
GraphicsDevice.BlendState = BlendState.Additive;
foreach (EffectPass pass in postProcessEffect.CurrentTechnique.Passes)
{
pass.Apply();
spriteBatch.Draw(
bulletRenderTarget,
new Vector2(0,0),
Color.White
);
}
DrawHud();
foreach (BaseEntity entity in entityList)
{
entity.Draw(gameTime);
}
spriteBatch.End();
I'm only trying to get the bullets to glow.
shader :
float BlurDistance = 0.002f;
sampler ColorMapSampler : register(s1);
float4 PixelShaderFunction(float2 Tex: TEXCOORD0) : COLOR
{
float4 Color;
// Get the texel from ColorMapSampler using a modified texture coordinate. This
// gets the texels at the neighbour texels and adds it to Color.
Color = tex2D( ColorMapSampler, float2(Tex.x+BlurDistance, Tex.y+BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x-BlurDistance, Tex.y-BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x+BlurDistance, Tex.y-BlurDistance));
Color += tex2D( ColorMapSampler, float2(Tex.x-BlurDistance, Tex.y+BlurDistance));
// We need to devide the color with the amount of times we added
// a color to it, in this case 4, to get the avg. color
Color = Color / 4;
// returned the blurred color
return Color;
}
technique Blur
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
The reason it's purple is because you have
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
which should be the other way around, so changing that into
GraphicsDevice.SetRenderTarget(bulletRenderTarget);
GraphicsDevice.Clear(Color.Black);
fixes the purple problem, to fix the shader change the following in the fx file
sampler ColorMapSampler : register(s0);
And change your spriteBatch.Begin() into
spriteBatch.Begin(SpriteSortMode.Immediate, null);
Some extra info:
the s0 points to the first texture on the graphics device, which is the one that supplied by spriteBatch.Draw, if you want to use s1 you'd have to set it on the GraphicsDevice first by using:
GraphicsDevice.Textures[1] = bulletRenderTarget;
the SpriteSortMode.Immediate just forces the spriteBatch.Draw to draw the sprite immediately, if you don't set it it'll create a batch and draw them all at once, but this will be to late because it needs to be drawn when the EffectPass is being applied.
As for the blur you could lower the value of BlurDistance, but you'd have to try, you can also look up how to do a bloom shader, usually gives a nice effect too.
Related
I think I am very stupid because I can't figure out what causes the issue in this simplest case.
Project type is Windows OpenGL (on Android the issue also persists).
Here is the shader:
//----------------------------------------------------------------------------
float4 PSTest (float2 TexCoords : TEXCOORD0) : COLOR0
{
float4 color = float4 (1, 0, 0, 1);
return color;
}
//----------------------------------------------------------------------------
Technique Test
{
pass Test
{
PixelShader = compile ps_2_0 PSTest();
}
}
It works fine if I draw it to the screen with the Texture (128x128):
Shader.CurrentTechnique = Shader.Techniques["Test"];
GraphicsDevice.SetRenderTarget (null);
GraphicsDevice.Clear (Color.White);
Batch.Begin (SpriteSortMode.Immediate, BlendState.Opaque,
SamplerState.PointClamp, DepthStencilState.None, null, Shader);
Batch.Draw (Texture, Vector2.Zero, Color.White);
Batch.End();
Now I decide to draw it into render target Output created by code:
new RenderTarget2D (GraphicsDevice, 256, 256, false,
SurfaceFormat.Color, DepthFormat.None);
Draw code:
// Render into Output
Shader.CurrentTechnique = Shader.Techniques["Test"];
GraphicsDevice.SetRenderTarget (Output);
GraphicsDevice.Clear (Color.DimGray);
Batch.Begin (SpriteSortMode.Immediate, BlendState.Opaque,
SamplerState.PointClamp, DepthStencilState.None, null, Shader);
Batch.Draw (Texture, Vector2.Zero, Color.White);
Batch.End();
// Draw Output
GraphicsDevice.SetRenderTarget (null);
GraphicsDevice.Clear (Color.White);
Batch.Begin();
Batch.Draw (Output, Vector2.Zero, Color.White);
Batch.End();
But the result is totally absurd:
I have tried different simplest shaders, SurfaceFormat.HdrBlendable, texture sizes - but nothing helps.
Also I have tried to draw Texture (filled with violet) into render target without shader (then to the screen) and it works fine.
It seems like I have missed some basics thing about shaders in XNA but I can't figure out what it is.
Here is the solution: https://monogame.codeplex.com/discussions/530086. The reason is in the shader's input parameters.
The sample
If you watch the code, I'm interested in refraction.fx, and in void DrawRefractGlacier(GameTime gameTime) function. Here you can notice that the function uses a texture to render water distortion on an image (waterfall.jpg as "distorter image", and glacier.jpg as distorted image).
If you read inside refraction.fx, at the beginning it says:
// Effect uses a scrolling displacement texture to offset the position of the main
// texture. Depending on the contents of the displacement texture, this can give a
// wide range of refraction, rippling, warping, and swirling type effects.
It seems that would be easy to achieve another effect by changing the image. I tried that with an image like this:
I want to achieve the effect of distorting everything around as a rotating whirl, or a spiral. How can I do that?
Some simple sequential screen of how it looks with my texture:
Refraction shader:
// Effect uses a scrolling displacement texture to offset the position of the main
// texture. Depending on the contents of the displacement texture, this can give a
// wide range of refraction, rippling, warping, and swirling type effects.
float2 DisplacementScroll;
float2 angle;
sampler TextureSampler : register(s0);
sampler DisplacementSampler : register(s1);
float2x2 RotationMatrix(float rotation)
{
float c = cos(rotation);
float s = sin(rotation);
return float2x2(c, -s, s ,c);
}
float4 main(float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
float2 rotated_texcoord = texCoord;
rotated_texcoord -= float2(0.25, 0.25);
rotated_texcoord = mul(rotated_texcoord, RotationMatrix(angle));
rotated_texcoord += float2(0.25, 0.25);
float2 DispScroll = DisplacementScroll;
// Look up the displacement amount.
float2 displacement = tex2D(DisplacementSampler, DispScroll+ texCoord / 3);
// Offset the main texture coordinates.
texCoord += displacement * 0.2 - 0.15;
// Look up into the main texture.
return tex2D(TextureSampler, texCoord) * color;
}
technique Refraction
{
pass Pass1
{
PixelShader = compile ps_2_0 main();
}
}
Its draw call:
void DrawRefractGlacier(GameTime gameTime)
{
// Set an effect parameter to make the
// displacement texture scroll in a giant circle.
refractionEffect.Parameters["DisplacementScroll"].SetValue(
MoveInCircle(gameTime, 0.2f));
// Set the displacement texture.
graphics.GraphicsDevice.Textures[1] = waterfallTexture;
// Begin the sprite batch.
spriteBatch.Begin(0, null, null, null, null, refractionEffect);
// Because the effect will displace the texture coordinates before
// sampling the main texture, the coordinates could sometimes go right
// off the edges of the texture, which looks ugly. To prevent this, we
// adjust our sprite source region to leave a little border around the
// edge of the texture. The displacement effect will then just move the
// texture coordinates into this border region, without ever hitting
// the edge of the texture.
Rectangle croppedGlacier = new Rectangle(32, 32,
glacierTexture.Width - 64,
glacierTexture.Height - 64);
spriteBatch.Draw(glacierTexture,
GraphicsDevice.Viewport.Bounds,
croppedGlacier,
Color.White);
// End the sprite batch.
spriteBatch.End();
}
I've got drawing sprites to work with OpenTK in my 2d game engine now. Only problem I'm having is that custom drawn objects with opengl (anything but sprites really) show up as the background color. Example:
I'm Drawing a 2.4f width black line here. There's also a quad and a point in the example, but they do not overlap anything that's actually visible. The line overlaps the magenta sprite, but the color is just wrong. My question is: Am I missing an OpenGL feature, or doing something horrible wrong?
These are the samples of my project concerning drawing: (you can also find the project on https://github.com/Villermen/HatlessEngine if there's questions about the code)
Initialization:
Window = new GameWindow(windowSize.Width, windowSize.Height);
//OpenGL initialization
GL.Enable(EnableCap.PointSmooth);
GL.Hint(HintTarget.PointSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.LineSmooth);
GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.ClearColor(Color.Gray);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Lequal);
GL.ClearDepth(1d);
GL.DepthRange(1d, 0d); //does not seem right, but it works (see it as duct-tape)
Every draw cycle:
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
//reset depth and color to be consistent over multiple frames
DrawX.Depth = 0;
DrawX.DefaultColor = Color.Black;
foreach(View view in Resources.Views)
{
CurrentDrawArea = view.Area;
GL.Viewport((int)view.Viewport.Left * Window.Width, (int)view.Viewport.Top * Window.Height, (int)view.Viewport.Right * Window.Width, (int)view.Viewport.Bottom * Window.Height);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(view.Area.Left, view.Area.Right, view.Area.Bottom, view.Area.Top, -1f, 1f);
GL.MatrixMode(MatrixMode.Modelview);
//drawing
foreach (LogicalObject obj in Resources.Objects)
{
//set view's coords for clipping?
obj.Draw();
}
}
GL.Flush();
Window.Context.SwapBuffers();
DrawX.Line:
public static void Line(PointF pos1, PointF pos2, Color color, float width = 1)
{
RectangleF lineRectangle = new RectangleF(pos1.X, pos1.Y, pos2.X - pos1.X, pos2.Y - pos1.Y);
if (lineRectangle.IntersectsWith(Game.CurrentDrawArea))
{
GL.LineWidth(width);
GL.Color3(color);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(pos1.X, pos1.Y, GLDepth);
GL.Vertex3(pos2.X, pos2.Y, GLDepth);
GL.End();
}
}
Edit: If I disable the blendcap before and enable it after drawing the line it does show up with the right color, but I must have it blended.
I forgot to unbind the texture in the texture-drawing method...
GL.BindTexture(TextureTarget.Texture2D, 0);
Hello everyone I'm currently trying to create a deferred renderer for my graphics engine using c# and SlimDX. As a resource I use this tutorial which is very helpful eventhough it's intended for XNA.
But right now I'm stuck...
I have my renderer set up to draw all geometry's color, normals and depth to seperate render target textures. This works. I can draw the resulting textures to the restored backbuffer as sprites and I can see that they contain just what they are supposed to. But when I try to pass those Textures to another shader, in this case to create a light map, weirds things happen. Here's how I draw one frame:
public bool RenderFrame(FrameInfo fInfo){
if(!BeginRender()) //checks Device, resizes buffers, calls BeginScene(), etc.
return false;
foreach(RenderQueue queue in fInfo.GetRenderQueues()){
RenderQueue(queue);
}
EndRender(); //currently only calls EndScene, used to do more
ResolveGBuffer();
DrawDirectionalLight(
new Vector3(1f, -1f, 0),
new Color4(1f,1f,1f,1f),
fi.CameraPosition,
SlimMath.Matrix.Invert(fi.ViewProjectionMatrix));
}
private void ResolveGBuffer() {
if(DeviceContext9 == null || DeviceContext9.Device == null)
return;
DeviceContext9.Device.SetRenderTarget(0, _backbuffer);
DeviceContext9.Device.SetRenderTarget(1, null);
DeviceContext9.Device.SetRenderTarget(2, null);
}
private void DrawDirectionalLight(Vector3 lightDirection, Color4 color, SlimMath.Vector3 cameraPosition, SlimMath.Matrix invertedViewProjection) {
if(DeviceContext9 == null || DeviceContext9.Device == null)
return;
DeviceContext9.Device.BeginScene();
_directionalLightShader.Shader.SetTexture(
_directionalLightShader.Parameters["ColorMap"],
_colorTexture);
_directionalLightShader.Shader.SetTexture(
_directionalLightShader.Parameters["NormalMap"],
_normalTexture);
_directionalLightShader.Shader.SetTexture(
_directionalLightShader.Parameters["DepthMap"],
_depthTexture);
_directionalLightShader.Shader.SetValue<Vector3>(
_directionalLightShader.Parameters["lightDirection"],
lightDirection);
_directionalLightShader.Shader.SetValue<Color4>(
_directionalLightShader.Parameters["Color"],
color);
_directionalLightShader.Shader.SetValue<SlimMath.Vector3>(
_directionalLightShader.Parameters["cameraPosition"],
cameraPosition);
_directionalLightShader.Shader.SetValue<SlimMath.Matrix>(
_directionalLightShader.Parameters["InvertViewProjection"],
invertedViewProjection);
_directionalLightShader.Shader.SetValue<Vector2>(
_directionalLightShader.Parameters["halfPixel"],
_halfPixel);
_directionalLightShader.Shader.Technique =
_directionalLightShader.Technique("Technique0");
_directionalLightShader.Shader.Begin();
_directionalLightShader.Shader.BeginPass(0);
RenderQuad(SlimMath.Vector2.One * -1, SlimMath.Vector2.One);
_directionalLightShader.Shader.EndPass();
_directionalLightShader.Shader.End();
DeviceContext9.Device.EndScene();
}
Now when I replace the call to DrawDirectionalLight with some code to draw _colorTexture, _normalTexture and _depthTexture to the screen everything looks ok, but when I use the DrawDirectionalLight function instead I see wild flickering. From the output of PIX it looks like my textures do not get passed to the shader correctly:
Following the tutorial the texture parameters and samplers are defined as follows:
float3 lightDirection;
float3 Color;
float3 cameraPosition;
float4x4 InvertViewProjection;
texture ColorMap;
texture NormalMap;
texture DepthMap;
sampler colorSampler = sampler_state{
Texture = ColorMap;
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter= LINEAR;
MinFilter= LINEAR;
MipFilter= LINEAR;
};
sampler depthSampler = sampler_state{
Texture = DepthMap;
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter= POINT;
MinFilter= POINT;
MipFilter= POINT;
};
sampler normalSampler = sampler_state{
Texture = NormalMap;
AddressU = CLAMP;
AddressV = CLAMP;
MagFilter= POINT;
MinFilter= POINT;
MipFilter= POINT;
};
Now my big question is WHY? There are no error messages printed to debug output.
EDIT:
the rendertargets/textures are created like this:
_colorTexture = new Texture(DeviceContext9.Device,
DeviceContext9.PresentParameters.BackBufferWidth,
DeviceContext9.PresentParameters.BackBufferHeight,
1,
Usage.RenderTarget,
Format.A8R8G8B8,
Pool.Default);
_colorSurface = _colorTexture.GetSurfaceLevel(0);
_normalTexture = new Texture(DeviceContext9.Device,
DeviceContext9.PresentParameters.BackBufferWidth,
DeviceContext9.PresentParameters.BackBufferHeight,
1,
Usage.RenderTarget,
Format.A8R8G8B8,
Pool.Default);
_normalSurface = _normalTexture.GetSurfaceLevel(0);
_depthTexture = new Texture(DeviceContext9.Device,
DeviceContext9.PresentParameters.BackBufferWidth,
DeviceContext9.PresentParameters.BackBufferHeight,
1,
Usage.RenderTarget,
Format.A8R8G8B8,
Pool.Default);
_depthSurface = _depthTexture.GetSurfaceLevel(0);
EDIT 2:
The problems seems to lie in the directionalLightShader itselft since passing other regular textures doesn't work either.
The answer to my problem is as simple as the problem was stupid. The strange behaviour was caused by 2 different errors:
I was just looking at the wrong events in PIX. The textures we passed correctly to the shader but I didn't see it because it was 'hidden' in the BeginPass-event (behind the '+').
The pixel shader which I was trying to execute never got called because vertices of the fullscreen quad I used to render were drawn in clockwise order... my CullMode was also set to clockwise...
Thanks to everyone who read this question!
I load my textures using
Texture2D.FromFile()
then draw them using
spriteBatch.Draw()
But here's the point: I want to change some colors of the image to another ones. So my questions:
How to change single color of the image to another single color (eg. blue to red).
In fact, what I really want to do is changing group of colors to another group of colors. For example red and similar hues to red to blue and similar hues to blue. You can do this for example in Corel PHOTO-PAINT ("Replace Color").
Please have in mind, that I'm a beginner in XNA.
Best regards,
Jack
EDIT:
Thank you very much for help, guys. Callum's answer is very helpful indeed. But I'm wondering is there a built-in function to solve my second problem, because writing your own may be time-consuming. And I think, that kind of function may be very useful. Something like:
color.SetNewColor(Color color_from, Color color_to, int range)
That kind of function, as I've said before, is built in Corel PHOTO-PAINT. To explain it better, here is the example of what I'm talking about:
link text
So, I only set color_from, color_to and range. I think it works like that: it checks every color of the image, if it is in range of color_from, it is changed to adequate color in hue of color_to.
I assume you mean change individual pixels? In that case use the GetData() and SetData() methods of the Texture2D class.
For example, you can get an array containing the colours of the individual pixels by doing this:
// Assume you have a Texture2D called texture
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
// You now have a packed array of Colors.
// So, change the 3rd pixel from the right which is the 4th pixel from the top do:
data[4*texture.Width+3] = Color.Red;
// Once you have finished changing data, set it back to the texture:
texture.SetData(data);
Note you can use the other overloads of GetData() to select only a section.
So, to replace each pixel of a specified colour to another colour:
// Assume you have a Texture2D called texture, Colors called colorFrom, colorTo
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
for(int i = 0; i < data.Length; i++)
if(data[i] == colorFrom)
data[i] = colorTo;
texture.SetData(data);
To see if hues are similar, try this method:
private bool IsSimilar(Color original, Color test, int redDelta, int blueDelta, int greenDelta)
{
return Math.Abs(original.R - test.R) < redDelta && Math.Abs(original.G - test.G) < greenDelta && Math.Abs(original.B - test.B) < blueDelta;
}
where *delta is the tolerance of change for each colour channel that you want to accept.
To answer your edit, no there is a built in function, but you can just use a mixture of ideas from the two sections above:
Color[] data = new Color[texture.Width * texture.Height];
texutre.GetData(data);
for(int i = 0; i < data.Length; i++)
if(IsSimilar(data[i], colorFrom, range, range, range))
data[i] = colorTo;
texture.SetData(data);
Moving data between the GPU and CPU by using GetData and SetData is an expensive operation. If there are a limited number of colors, you could use a pixel shader effect when rendering to the screen. You can pass an effect to SpriteBatch.Begin:
sampler2D input : register(s0);
/// <summary>The color used to tint the input.</summary>
/// <defaultValue>White</defaultValue>
float4 FromColor : register(C0);
/// <summary>The color used to tint the input.</summary>
/// <defaultValue>Red</defaultValue>
float4 ToColor : register(C1);
/// <summary>Explain the purpose of this variable.</summary>
/// <minValue>05/minValue>
/// <maxValue>10</maxValue>
/// <defaultValue>3.5</defaultValue>
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
Color= tex2D(input , uv.xy);
if (Color.r == FromColor.r && Color.g == FromColor.g && Color.b == FromColor.b)
return ToColor;
return Color;
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 main();
}
}
Create your effect in your LoadContent method:
colorSwapEffect = Content.Load<Effect>(#"Effects\ColorSwap");
colorSwapEffect.Parameters["FromColor"].SetValue(Color.White);
colorSwapEffect.Parameters["ToColor"].SetValue(Color.Red);
And pass the effect to your call to SpriteBatch.Begin():
sprite.Begin(0, BlendState.Opaque, SamplerState.PointWrap,
DepthStencilState.Default, RasterizerState.CullNone, colorSwapEffect);
For what you really want to do, you can swap the red and blue channels even more easily. Change your pixel shader's main() function to this, which swaps b (blue) and r (red):
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 Color;
Color= tex2D(input , uv.xy);
return float4(Color.b, Color.g, Color.r, Color.a);
}
Callum's solution is powerful and flexible.
A more limited solution that is slightly easier to implement is to leverage the spriteBatch color parameter.
The variables
Texture2D sprite; //Assuming you have loaded this somewhere
Color color = Color.Red; //The color you want to use
Vector2 position = new Vector2(0f, 0f); //the position to draw the sprite
The drawing code
//Start the spriteBatch segment, enable alpha blending for transparency
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
//Draw our sprite at the specified position using a specified color
spriteBatch.Draw(sprite, position, color);
//end the spritebatch
spriteBatch.End();
If your sprite is all white, then using this method will turn your sprite red. Also, make sure you are using a file format with transparency in it, PNG is a favorite.
Callum hit it on the head if you are changing the color of 2D images as it seems you are - but as you can see you actually need to determine the actual pixel you want to modify and edit it rather than "replace yellow with green" for example.
The same logic could be used to do this replacement (simply loop through the pixels of the image and check the color - I can say that be wary when editing textures like this though as they seemed to cause some pretty serious spikes in performance depending on what was done and how often. I didn't fully investigate but I think it was causing quite a bit of garbage collection.
this works for me:
protected override void Initialize()
{
sprite = Content.Load<Texture2D>("Parado");
Color[] data = new Color[sprite.Width * sprite.Height];
sprite.GetData(data);
// new color
Color novaCor =Color.Blue;
for (int i = 0; i < data.Length; i++)
{
// cor roxa no desenho
if (data[i].R == 142
&& data[i].G == 24
&& data[i].B == 115)
{
data[i] = novaCor;
}
}
sprite.SetData<Color>(data);
posicaoNinja = new Vector2(0, 200);
base.Initialize();
}