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
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.
I have a flower of which the petals are individually from the stalk:
I have no problem to change the petals to green or black with following script:
private void Start()
{
flower = transform.GetChild(transform.childCount - 1).gameObject;
if (Random.Range(0, 1.0f) < 0.5f)
{
for (int i = 0; i < flower.transform.childCount; i++)
{
flower.transform.GetChild(i).gameObject.GetComponent<SpriteRenderer>().color = Color.green;// Color.yellow;
}
}
}
But what is weird is that the color will not change to yellow with this:
flower.transform.GetChild(i).gameObject.GetComponent<SpriteRenderer>().color = Color.yellow;
Note that, the Random.range() is not the problem. It just couldn't change to yellow/ or white, but colors such as black, green will be ok.
And if you may forgive me to ask another question, is the color representation of rgba from 0-1 is that of in Photoshop representation 0-255 divided by 255?
Such that a color RGBA(255,255,1,255) will be Color(255/255.0, 255/255.0, 1/255.0, 255/255.0) which will come to Color(1.0f,1.0f,0.00001f, 1.0f) in Unity?
FYI, the attribute of the petals' spriteRenderer is as following, and I use the Sprite-Default material:
EDIT:
Add the raw sprite sheet as required by comment:
You need to make the petals of the flower sprite white. (i.e change them in your sprite editing software, NOT in Unity). This will allow you to change the color of the sprite in the editor.
To check this, try just changing the color of the Sprite Renderer component to yellow in the inspector. It should still be red. Also check if the Sprite Renderer component's color field is yellow at run time. This will allow you to be certain that the code is working.
The problem is not your code; the problem is how the sprite's shader works. The sprite-default shader works by multiplying the color field that you are changing by the actual RGB value of the sprite. If the value of one of the RGB fields is 0 in the sprite's texture then multiplying it by some value will mean that it is still 0. I think that is what is happening for you.
You can see this happening by importing a pure red sprite and then trying to change the color of it in the inspector. It will just turn black.
If you are curious, you can download the source code for Unity's built in shaders Here.
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)
I don't know if this is possible with Monotouch so I thought I'd ask the experts. Let's say I want to be able to take a picture of a painted wall and recognize the general color from it - how would I go about doing that in C#/Monotouch?
I know I need to capture the image and do some image processing but I'm more curious about the dynamics of it. Would I need to worry about lighting conditions? I assume the flash would "wash out" my image, right?
Also, I dont need to know exact colors, I just need to know the general color family. I dont need to know a wall is royal blue, I just need it to return "blue". I dont need to know hunter green, I just need it to return "green". I've never done that with image processing.
The code below relies on the .NET System.Drawing.Bitmap class and the System.Drawing.Color class, but I believe these are both supported in MonoTouch (at least based on my reading of the Mono Documentation).
So assuming you have an image in a System.Drawing.Bitmap object named bmp. You can obtain the average hue of that image with code like this:
float hue = 0;
int w = bmp.Width;
int h = bmp.Height;
for (int y = 0; y < bmp.Height; y++) {
for (int x = 0; x < bmp.Width; x++) {
Color c = bmp.GetPixel(x, y);
hue += c.GetHue();
}
}
hue /= (bmp.Width*bmp.Height);
That's iterating over the entire image which may be quite slow for a large image. If performance is an issue, you may want to limit the pixels evaluated to a smaller subsection of the image (as suggested by juhan_h), or just use a smaller image to start with.
Then given the average hue, which is in the range 0 to 360 degrees, you can map that number to a color name with something like this:
String[] hueNames = new String[] {
"red","orange","yellow","green","cyan","blue","purple","pink"
};
float[] hueValues = new float[] {
18, 54, 72, 150, 204, 264, 294, 336
};
String hueName = hueNames[0];
for (int i = 0; i < hueNames.Length; i++) {
if (hue < hueValues[i]) {
hueName = hueNames[i];
break;
}
}
I've just estimated some values for the hueValues and hueNames tables, so you may want to adjust those tables to suit your requirements. The values are the point at which the color appears to change to the next name (e.g. the dividing line between red and orange occurs at around 18 degrees).
To get an idea of the range of colors represent by the hue values, look at the color wheel below. Starting at the top it goes from red/orange (around 0° - north) to yellow/green (around 90° - east), to cyan (around 180° - south), to blue/purple (around 270° - west).
You should note, however, that we are ignoring the saturation and brightness levels, so the results of this calculation will be less than ideal on faded colors and under low light conditions. However, if all you are interested in is the general color of the wall, I think it might be adequate for your needs.
I recently dealt with shifting white balance on iOS (original question here: iOS White point/white balance adjustment examples/suggestions) which included a similar problem.
I can not give you code samples in C# but here are the steps that I would take:
Capture the image
Decide what point/part of the image is of interest (the smaller the better)
Calculate the "color" of that point of the image
Convert the "color" to human readable form (I guess that is what you need?)
To accomplish step #2 I would either let the user choose the point or take the point to be in the center of the image, because that is usually the place to which the camera is actually pointed.
How to accomplys step #3 depends on how big is the area chosen in step #2. If the area is 1x1 pixels then you render it in RGB and get the component (ie red green and blue) values from that rendered pixel. If the area is larger then you would need to get the RGB values of each of the pixels contained in that area and average them.
If you only need a general color this would be mostly it. But if you need to compensate for lighting conditions the problem gets very much more complicated. To compensate for lighting (ie White Balancing) you need to do some transformations and some guesses in which conditions the photo was taken. I will not go into details (I wrote my Bachelors thesis on those details) but Wikipedias article on White Balance is a good starting point.
It is also worth to note that the solution to White Balancing problem will always be subjective and dependent on the guesses made in which light the photo was taken (at least as far as I know).
To accomplish step #4 you should search for tables that map RGB values to human-readable colors. I have not had the need for these kinds of tables, but I am sure they exist somwhere on the Internet.
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.