In my 2D tile based game, the player can paint tiles to change there color. The simple approach (and what I have already done) is to use the tint parameter in SpriteBatch.Draw.
This looks nice, and here is an example:
But say, I want to paint the wood white. If you have ever messed with the tint, you know this isn't possible using the tint parameter. Color.White will tint the sprite the default color, and not white. This isn't a major problem, but I suppose some users might want it.
My question is, is there a method to color a sprite based on hue/saturation instead of tint. Similar to the "colorify" function in GIMP.
An obvious approach would to be to use this function in GIMP and just export sprites for each color. The trouble comes in at the fact that this would take forever to do for all my sprites, and each sprite has multiple variations, meaning you could have in total 100+ combos for one block type.
Is this possible? I suppose a shader might get the job done.
The "colourify" function in The GIMP simply does a desaturate (convert to grayscale), followed by a colour multiplication. Very simple. This should be the equivalent HLSL (untested):
float4 original = tex2d(...);
float q = (original.r + original.g + original.b) / 3;
q /= original.a; // optional - undo alpha premultiplication
return float4(tint.rgb * q, tint.a * original.a);
But you could achieve the same effect by simply storing all your textures desaturated to begin with and using the default SpriteBatch shader for its multiplication. If you don't want to modify your source art, you could do it in a custom content processor.
Although, if you want to use a custom shader, you can do something more sophisticated. You could implement a full hue-rotation (kinda complex). But perhaps you could consider something like the "Overlay" blend mode (very simple) - which lets you colourize the grays, while preserving both the highlights and lowlights (instead of multiply, which also colourizes the highlights).
To continue using the tint in SPriteBatch.Draw. Just make your "paintable" textures grayscale. So the white wood would be your default, but you draw it with a tint that makes it wood-colored.
I often use this to make UI and team coloring :)
in case it is interesting, what the tint does is just to multiply each pixel in the texture by the color you choose.
example:
texture pixel is (rgba) [1.0, 0.5, 0.5, 1.0]
tint is [1.0, 0.5, 0.5, 0.5]
result is [1.0, 0.25, 0.25, 0.5] (half transparent and more red-ish)
Related
So I'm looking to create an effect of having a bubble around my player which, when he enters a hidden area (hidden by tilemaps) the bubble activates and it essentially has an xray effect. So I can see the background, the ground and all the items inside the area I just can't see the blocks themselves.
So pretty much going from this
To this
And as I go further in the more gets revealed
I have no idea what to even begin searching for this. So any direction would be greatly appreciated
First of all, I want to get something out of the way: Making things appear when they are nearby the player is easy, you use a light and a shader. Making things disappear when they are nearby the player by that approach is impossible in 2D (3D has flags_use_shadow_to_opacity).
This is the plan: We are going to create a texture that will work as mask for what to show and what not to show. Then we will use that texture mask with a shader to make a material that selectively disappears. To create that texture, we are going to use a Viewport, so we can get a ViewportTexture from it.
The Viewport setup is like this:
Viewport
├ ColorRect
└ Sprite
Set the Viewport with the following properties:
Size: give it the window size (the default is 1024 by 600)
Hdr: disable
Disable 3D: enable
Usage: 2D
Update mode: Always
For the Sprite you want a grayscale texture, perhaps with transparency. It will be the shape you want to reveal around the player.
And for the ColorRect you want to set the background color as either black or white. Whatever is the opposite of the color on the Sprite.
Next, you are going to attach a script to the Viewport. It has to deal with two concerns:
Move the Sprite to match the position of the player. That looks like this:
extends Viewport
export var target_path:NodePath
func _process(_delta:float) -> void:
var target := get_node_or_null(target_path) as Node2D
if target == null:
return
$Sprite.position = target.get_viewport().get_canvas_transform().origin
And you are going to set the target_path to reference the player avatar.
In this code target.get_viewport().get_canvas_transform().origin will give us the position of the target node (the player avatar) on the screen. And we are placing the Sprite to match.
Handle window resizes. That looks like this:
func _ready():
# warning-ignore:return_value_discarded
get_tree().get_root().connect("size_changed", self, "_on_size_changed")
func _on_size_changed():
size = get_tree().get_root().size
In this code we connect to the "size_changed" of the root Viewport (the one associated with the Window), and change the size of this Viewport to match.
The next thing is the shader. Go to your TileMap or whatever you want to make disappear and add a shader material. This is the code for it:
shader_type canvas_item;
uniform sampler2D mask;
void fragment()
{
COLOR.rgb = texture(TEXTURE, UV).rgb;
COLOR.a = texture(mask, SCREEN_UV).r;
}
As you can see, the first line will be setting the red, green, and blue channels to match the texture the node already has. But the alpha channel will be set to one of the channels (the red one in this case) of the mask texture.
Note: The above code will make whatever is in the black parts fully invisible, and whatever is in the white parts fully visible. If you want to invert that, change COLOR.a = texture(mask, SCREEN_UV).r; to COLOR.a = 1.0 - texture(mask, SCREEN_UV).r;.
We, of course, need to set that mask texture. After you set that code, there should be a shader param under the shader material called "Mask", set it to a new ViewportTexture and set the Viewport to the one we set before.
And we are done.
I tested this with this texture from publicdomainvectors.org:
Plus some tiles from Kenney. They are all, of course, under public domain.
This is how it looks like:
Experiment with different textures for different results. Also, you can add a shader to the Sprite for extra effect. For example add some ripples, by giving a shader material to the Sprite with code like this one:
shader_type canvas_item;
void fragment()
{
float width = SCREEN_PIXEL_SIZE.x * 16.0;
COLOR = texture(TEXTURE, vec2(UV.x + sin(UV.y * 32.0 + TIME * 2.0) * width, UV.y));
}
So you get this result:
There is an instant when the above animation stutters. That is because I didn't cut the loop perfectly. Not an issue in game. Also the animation has much less frames per second than the game would.
Addendum A couple things I want to add:
You can create a texture by other means. I have a couple other answer where I cover some of it
How can I bake 2D sprites in Godot at runtime? where we use blit_rect. You might also be interested in blit_rect_mask.
Godot repeating breaks script where we are using lockbits.
I wrote a shader that outputs on the alpha channel here. Other options include:
Using BackBufferCopy.
To discard fragments.
So, I'm trying to make a game in XNA, and I'm applying a pixel shader to my level so I can add effects to the gameplay screen. The shader itself is working nicely, but it has one flaw that really pushes back performance. As long as the shader is active, anything I draw to the screen acts as though it was drawn with it's alpha channel at the maximum. I'm drawing a lot of things using XNA's built in Color class, and much of it has varying alpha. However, the shader seems to ignore all of this. This occurs even if I just return the source from the shader.
I've seen a few possible solutions. The first of these was to draw to a RenderTarget2D and, while this did solve this issue, it caused more serious problems with alpha that I don't even want to go into.
I have seen a few people claim that multiplying the colours by the alpha value in the shader seems to help, but that made absolutely no difference, which suggests to me that the alpha value input is always being read as 1.
Here are some images to illustrate my problem:
Hopefully, someone knows how to resolve this.
In your Draw() method, you need to call:
GraphicsDevice.BlendState = BlendState.NonPremultiplied; before calling any methods that invoke your shader (i.e. DrawUserIndexedPrimitives, etc.).
Otherwise, the framework assumes the alpha value has been premultiplied and that it can just blit the RGB values right to the framebuffer.
I want to know how to remove part of a Texture from a Texture2D.
I have a simple game in which I want to blow up a planet piece by piece, when a bullet hits it "digs" into the planet.
The physics are already working but I am stuck on how to cut the texture properly.
I need to create a function that takes a Texture2D a position and a radius as input and returns the new Texture2D.
Here is an example of the Texture2D before and after what I want to accomplish.
http://img513.imageshack.us/img513/6749/redplanet512examplesmal.png
Also note that i drew a thin brown border around the crater hole. If this is possible it would be a great bonus.
After doing alot of googling on the subject it seems the best and fastest way to achieve the effect i want is to use pixel shaders.
More specifically a shader method called 'Alpha mapping'. Alpha mapping is done by using the original texture and another greyscale texture that defines what parts are visible or not.
The idea of the shader is to go through each pixel in the original texture and check how black each pixel in the greyscale image is at the same coordinate. The blacker the pixel in the greyscale picture is the higher the alpha value (more visible) the pixel in the original texture becomes. Since all this is done on the GPU it is lightning fast and leaves the CPU ready to do the actual logic for the game.
For my example I will create a black image to use as my greyscale image and then draw white circles on this corresponding to the parts i want to remove.
I've found a MSDN examples with working source code for XNA 4 that does this (the cat example):
http://create.msdn.com/en-US/education/catalog/sample/sprite_effects
EDIT:
I got this to work quite nicely. Created a small tutorial with source code here: http://syntaxwarriors.com/2012/xna-alpha-mapping-with-pixel-shaders/
A good way of doing this is to render a "hole texture" using alphablend on top of your planet texture. Think of it like drawing an invisibility circle over your original texture.
Take a look at this thread for a few nice links worms-style-destructible-terrain.
To achieve your brown edges I'd guess you'd need to take a similar approach. First render the hole to your terrain with say radius 10px. Then you render another circle from the same origin point but with a slightly larger radius, say 12px. You'd then need to set this circle to a blendmode that results in a brown color.
look at my class here:
http://www.codeproject.com/Articles/328894/XNA-Sprite-Class-with-useful-methods
1.Simply create an object of Sprite class for your planet
Sprite PlanetSprite = new Sprite(PlanetTexture2D , new Vector2(//yourPlanet.X, //yourPlanet.Y));
2.when the bullet hits the planet, make a circle texure2d by the center of collision point using "GetCollisionPoint(Sprite b)" method
-you can have a Circle.png with transparent corners
-or you can create a Circle using math(which is better if you want to have bullet power)
3.then create an Sprite object of your circle
4.now use the "GetCollisionArea(Sprite b)" to get the overlapped area
5.now use the "ChangeBatchPixelColor(List pixels, Color color)" where pixels is the overlapped area and color is Color.FromNonPremultiplied(0, 0, 0, 0)
-note you don't need to draw your circle at all, after using it you can destroy it, or leave it for further use
I am developing a 2D game in C#/XNA where the sprites/backgrounds/etc are Texture2Ds. I am interested in rendering a particular scene in either "daytime" or "nighttime". Obviously one option would be to have two copies of every image, one darker than the other, but this isn't very scalable. Is there a way to adjust the colours on the fly so that they appear darker? I'd guess it would be easiest to adjust the brightness or something (or whichever would look best) than trying to apply some sort of uniform transform to RGB values, but that's a lesser issue at the moment. Is there any way to say "draw this Texture2D, but adjust all colours in this way by this amount"?
Thanks!
Check the overloads of SpriteBatch.Draw that accept a color parameter. It does exactly what you are looking for.
in your share
e.g.
ps_out ps( ps_in In )
{
PS_OUT Out = ( PS_OUT ) 0;
float4 color;
color = tex2D( sBase, In.Base.xy );
Out.Color = color;
return Out;
}
you could use alpha blending and then use the swizzle operator on the color member and multiply the alpha/opacity of the color.
e.g.
color.a = color.a * opacity / 255.0f
Give a Coordinate, how can I color a single pixel in XNA? i.e.
Coordinate(10,11).Color = Color.Red
If you're planning on doing a lot of pixels, for something like a particle system, it would be better to use a shader. You'll probably run into performance issues eventually just using a SpriteBatch.
There's two ways depending on what coordinates you mean:
For screen coordinates the easiest way is to have a Texture2D that holds nothing but a single white pixel, then drawing it with SpriteBatch and passing whatever color you want to the Draw method.
For 3D space coordinates you want to use a PointList.
There's a bit more complicated things you could do as well: use Texture2D.SetData to make your own single white pixel texture at run time. Or, it's also possible to use a PointList and project to screen space.