HLSL Unity5> Multiple render targets - unity3d

I am attempting to specify a frag output into a set texture (render target) depending on some logic.
To summarise my shader:
I am preforming a Texture3D raycasting method that allows for the user to 'see' inside the texture 3D data.
My issue arises when wanting to sample an area of this main texture and dump it into another of a smaller resolution (allowing for an eventual 'zooming' affect)
My research thus far has brought me to the use of Multiple render targets . in that (to my understanding) I would send this frag function output to another function which then outputs to set different targets.
Feedback appreciated;especially if there is an easier way to sample area into another texture (I have tried various compute shader methods- CPU methods are too slow) a CPU based analogy being the Unity's:
GetPixels function
Extract:
.float alpha is actually a raycast step result
.float4 t the colour plus alpha at mapping of the input Texture3D
._sample a pseudo bool flag for sampling
.texture3Dsampler (within commented if statement) is the smaller resolution Texture3D that I wish to write to; given the pixel being evaluated of the input Texture3D is within texture3Dsampler bounds from a certain start point - as shown in if statement logic.
float a = (1 - alpha);
float4 t = float4(t3d, a);
//Cn = Current pixel
int Cx = start.x;
int Cy = start.y;
int Cz = start.z;
if(_sample == 1 &&
((Cx >= _XSS) && (Cx <= (_XSS+_Tex3DSampled.x))) &&
((Cy >= _YSS) && (Cy <= (_XSS+_Tex3DSampled.y))) &&
((Cz >= _ZSS) && (Cz <= (_YSS+_Tex3DSampled.z)))
)
{
//render t into BOTH texture3D to screen output and texture3Dsampler output
}
else //if not sampling into other Texture3d, simple return to render t onto screen
{
return t; //returning into t
}
}

Related

Unity Compute Shader Texture Array Sampling

I've been struggling with this for while now and it is quite time critical so I have to ask here. I'm quite new to compute shaders but from what I've read, it is what I need for my usecase. I'm trying to find the total score from an array of textures, with the score being the product of each channel and a given weight. Previously, I was using NodeJS to do it but it doesn't scale as well given increasing the dimensions by 4 would increase the area required per texture by 16 and with multiple textures this isn't a good solution.
This is my compute shader right now:
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain
// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
SamplerState linearClampSampler;
float4 weights;
RWStructuredBuffer<Texture2DArray<float4>> scoreInput;
float output;
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
float4 result_mult = scoreInput[id.z].Sample(id.uv).rgba * weights.xyzw;
output = result_mult.r + result_mult.g + result_mult.b + result_mult.a;
}
For my C# dispatcher, I am doing:
string[] paths = new string[sessionData.masks.Length];
Texture2D[] textures = new Texture2D[sessionData.masks.Length];
for (int i = 0; i < sessionData.masks.Length; i++)
{
paths[i] = sessionData.masks[i].combinedMasks;
textures[i] = CustomUtility.LoadPNG(paths[i]);
}
int colourSize = sizeof(float) * 4;
ComputeBuffer wallBuffer = new ComputeBuffer(textures.Length, colourSize);
wallBuffer.SetData(textures);
CalculateScoreShader.SetBuffer(0, "scoreInput", wallBuffer);
CalculateScoreShader.Dispatch(0, 8,8,1);
I can't figure out how to sample the texture properly, and I want to make sure that I am setting up the buffer correctly for the shader to used like this. I also want to retrieve the output, but again I'm unsure how to do this.
I have looked through a decent amount of tutorials and documentation but I just can't seem to find the solution.

position check between object and point. Unity3D

if (((float) (Math.Round(gameObject.transform.position.x, 1))) == ((float) (Math.Round(array[i].x, 1))) && (((float) (Math.Round(gameObject.transform.position.y, 1))) == (float) (Math.Round(array[i].y, 1))))
Hello! I am using C#, and the array is filled with vector positions. I am trying to see when an object gets to a certain position. This never triggers. Do you have any other tips how to do this?
You are comparing float values directly ... never do that. It leads to a problem with the floating point precision. float values are internally actually stored in increments of an Epsilon.
see e.g. from here
The nearest float to 16.67 is 16.6700000762939453125
The nearest float to 100.02 is 100.01999664306640625
or here for a broader explenation.
Use Vector3.Distance!
Preferably with a certain threshold distance. There is an example that looks exactly like what you want to do in Coroutines (in JavaScript but the difference to c# in this case is minimal)
public float threshold = 0.1f;
//...
if(Vector3.Distance(gameObject.transform.position, array[i]) <= threshold)
{
....
}
Adjust threshold so it has a value that is bigger than what the object can possibly move between two frames.
Or together with Mathf.Approximately
if(Math.Approximately(Vector3.Distance(object.transform.position, array[i]), 0.0f))
{
....
}
if your threshold is smaller than 0.00001 than you could also use
if(object.transform.position == array[i])
{
....
}
since == uses <= 0.00001 for equality.
But note: In the most cases the last two options will also fail because the opts for a moving GameObject to match an exact 3D position are almost 0 unless you set fixed values somewhere.
Vector3.Distance also works with a Vector2 as parameter since an implicit typecast to Vector3 exists.

Trying to do collisions between two different classes

Sorry I'm not posting my code in here but the format was screwing up and it wouldn't post properly. I'm trying to make a game in which the player controls a ship and asteroids move in from the right side of the screen and the player has to avoid them. For the life of me I can;t figure out how to get the asteroids to collide with the player causing the player to lose and could use some assistance.
My Code for my game
You're pretty close.
In the function move() you calculate the new x and y position of the player:
void move() {
int r = d>>1;
sx = constrain(sx + v*(int(isRight) - int(isLeft)), r, width - r);
sy = constrain(sy + v*(int(isDown) - int(isUp)), r, height - r);
}
Next you have you check if the player position is in the bounds of the asteroid.
This is from you code in the Display() function:
void display() {
image(ship, sx, sy, 100, 50);
if (sx > a1x && sx < a1x && sy > a1y && sy < a1y){
image(explo, sx, sy, 200, 200);
}
This is in the right direction, but checks if both x and y of the player and the asteroid match exactly. This is very improbable, because it is the exact corner of both the player and the asteroid. Besides, the player might move more than 1 pixel per frame, causing it to skip the trigger.
Instead try this:
if (sx > a1x && sx < a1x +asteroid.aw && sy > a1y && sy < a1y+asteroid.ah)
This will check if the player x/y is in the bounds of the asteroid. And should get the result you want. However, this does not yet account for the height/width of the player spaceship. So you should also check
if (sx+player.width > a1x && sx+player.width< a1x +asteroid.aw && sy+player.height> a1y && sy+player.height< a1y+asteroid.ah)
Lastly I would make a general comment on your code. There is a lot of repetition. I assume you're learning, so that's okay, but allow me to give you some pointers that will make writing code a lot simpler: using ArrayLists and extending the use of objects
ArrayList<Asteroid> List_Of_Asteroids = new ArrayList<Asteroid>();
List_Of_Asteroids.add(new Asteroid(random(X1,X2), random(Y1,Y2))
This creates a list where you can add or delete asteroid objects. This makes it easy to do the same action for all asteroid, because you only have to type it once. Makes for less code as well. For instance:
player.move();
for (Asteroid rock : List_Of_Asteroids){
if (player.collisionCheck(rock)){
gameOver(); //triggers explosion image and subtracts life / ends game
}
}
display();
Just 5 lines to check all asteroids, even if there are 100. Everything that has to do with objects should be written in the object themselves. In your code the x/y position of the asteroids are kept in the main loop. You can easily divert those to the objects. If you look at the two code fragments above, you can access the asteroid x/y like this (inside the player class):
boolean collisionCheck(Asteroid a){
if (x > a.ax && x < a.ax+a.aw && y > a.ay && y < a.ay+a.ah){
return true;
else{
return false;}
Hope it helps!

Using compute shader to return whether texture has certain colour

I'm writing a Compute Shader (in the unity environment, which uses DirectX11 DirectCompute) which I need to do a very simple task: check whether any pixel in the image has green == 1 and blue > 0.5. (For clarity - the green channel is a 2d "light" and the blue channel is that lights "trail" - I want to detect whenever the light crosses back over its trail.)
I'm as far as displaying the overlap (shown in white) for debugging purposes, but I have no idea how to do something as simple as return a value indicating whether the texture actually contains an overlap. My confusion stems from how threads work. I have a float buffer with room for a single float - I simply need a 1 or 0.
For clarification the following two images show a "before" and "after" - all I need is a single "true" value telling me that some white exists in the second image.
The compute shader is as follows:
#pragma kernel CSMain
Texture2D<float4> InputTexture;
RWTexture2D<float4> OutputTexture;
RWStructuredBuffer<float> FloatBuffer;
[numthreads(8,8,1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
// need to detect any region where g == 1 and blue > 0.5
float green = InputTexture[id.xy].g;
float blue = round(InputTexture[id.xy].b);
float overlap = round((green + blue) / 2.0);
OutputTexture[id.xy] = float4(overlap, overlap, overlap, 1);
// answer here?? Note that the output texture is only for debugging purposes
FloatBuffer[0] = ??
}
You have the option of using atomic operation and count the pixels. You run your compute shader with one thread per pixel, and if the pixel meet the criteria, increment your rwbuffer.
Something like this :
Texture2D<float4> InputTexture;
RWBuffer<uint> NotAFloatBuffer;
[numthreads(8,8,1)]
void CSMain(uint3 id : SV_DispatchThreadID {
// need to detect any region where g == 1 and blue > 0.5
float green = InputTexture[id.xy].g;
float blue = round(InputTexture[id.xy].b);
float overlap = round((green + blue) / 2.0);
if (overlap > 0)
InterlockedAdd(NotAFloatBuffer[0],1);
}
In your case, you can stop here but atomics have some little cost penalties and often they are optimized by grouping the call from one single thread in your group with prior reduction, but this is only in the most extreme cases, you do not have to worry about that.

(Unity3D) Paint with soft brush (logic)

During the last few days i was coding a painting behavior for a game am working on, and am currently in a very advanced phase, i can say that i have 90% of the work done and working perfectly, now what i need to do is being able to draw with a "soft brush" cause for now it's like am painting with "pixel style" and that was totally expected cause that's what i wrote,
my current goal consist of using this solution :
import a brush texture, this image
create an array that contain all The alpha values of that texture
When drawing use the array elements in order to define the new pixels alpha
And this is my code to do that (it's not very long, there is too much comments)
//The main painting method
//theObject = the object to be painted
//tmpTexture = the object current texture
//targetTexture = the new texture
void paint (GameObject theObject, Texture2D tmpTexture, Texture2D targetTexture)
{
//x and y are 2 floats from another class
//they store the coordinates of the pixel
//that get hit by the RayCast
int x = (int)(coordinates.pixelPos.x);
int y = (int)(coordinates.pixelPos.y);
//iterate through a block of pixels that goes fro
//Y and X and go #brushHeight Pixels up
// and #brushWeight Pixels right
for (int tmpY = y; tmpY<y+brushHeight; tmpY++) {
for (int tmpX = x; tmpX<x+brushWidth; tmpX++) {
//check if the current pixel is different from the target pixel
if (tmpTexture.GetPixel (tmpX, tmpY) != targetTexture.GetPixel (tmpX, tmpY)) {
//create a temporary color from the target pixel at the given coordinates
Color tmpCol = targetTexture.GetPixel (tmpX, tmpY);
//change the alpha of that pixel based on the brush alpha
//myBrushAlpha is a 2 Dimensional array that contain
//the different Alpha values of the brush
//the substractions are to keep the index in range
if (myBrushAlpha [tmpY - y, tmpX - x].a > 0) {
tmpCol.a = myBrushAlpha [tmpY - y, tmpX - x].a;
}
//set the new pixel to the current texture
tmpTexture.SetPixel (tmpX, tmpY, tmpCol);
}
}
}
//Apply
tmpTexture.Apply ();
//change the object main texture
theObject.renderer.material.mainTexture = tmpTexture;
}
Now the fun (and bad) part is the code did exactly what i asked for, but there is something that i didn't think of and i couldn't solve after spend the whole night trying,
the thing is that by asking to draw anytime with the brush alpha i found myself create a very weird effect which is decreasing the alpha value of an "old" pixel, so i tried to fix that by adding an if statement that check if the current alpha of the pixel is less than the equivalent brush alpha pixel, if it is, then augment the alpha to be equal to the brush, and if the pixel alpha is bigger, then keep adding the brush alpha value to it in order to have that "soft brushing" effect, and in code it become this :
if (myBrushAlpha [tmpY - y, tmpX - x].a > tmpCol.a) {
tmpCol.a = myBrushAlpha [tmpY - y, tmpX - x].a;
} else {
tmpCol.a += myBrushAlpha [tmpY - y, tmpX - x].a;
}
But after i've done that, i got the "pixelized brush" effect back, am not sure but i think maybe it's because am making these conditions inside a for loop so everything is executed before the end of the current frame so i don't see the effect, could it be that ?
Am really lost here and hope that you can put me in the right direction,
Thank you very much and have a great day