- Home /
The question is answered, right answer was accepted
Can I get the whitest point of an image within a shader, and use it for white balancing the image?
I need to write an automatic white balance shader for a project, and for it to work, I need the whitest point of the image. I already tried to copy the RenderTexture into a Texture2D, but I got down to 7 FPS, since I'm doing this for every rendered image. I have to do this for every rendred image since it is a realtime realistic camera simulation, and performances are an important aspect of the project. I thought using the shader's properties to do so, and getting the white point in the C# using GetFloat after browsing the whole image, and sending it to the shader for the balancing in another pass, but since the RGB values needed change in the vertex shader, I have no way to keep the modified property and send it to the C# script, as the modified value stays on the GPU.
This question might be confusing, and I apologize for that.
Thanks in advance for your answers.
You cannot get whitest points in shader but you can get image that represent where the whitest point is like this.
You can see in the image that the center point is pure black. That work the same for texture. If you amplify the entire image RGB color you can get an image that only have the whitest point and any other color will be black.
To do that you have to make a custom shader which can be quite complicated.
I see... I'll try to find a way for the automatic white balance later, I'll focus on pre-deter$$anonymous$$ed white balance settings, which are quite easy to do. Thanks for your answer :)
Answer by Maxence_Marchand · May 16, 2018 at 11:17 AM
Well, found a way to do it, without a significant loss of performances, and it's quite easy. You need to copy the RenderTexture into a Texture2D, then create a Color32 array that you fill with GetPixels32, and you navigate through this array. Here's a code example of how to do it :
RenderTexture.active = src;
tex.ReadPixels(new Rect(0, 0, src.width, src.height), 0, 0);
Color32[] col = tex.GetPixels32();
int counter = -1;
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
counter++;
if (((float)(col[counter].r + col[counter].g + col[counter].b) / 3.0f) > average) {
r = col[counter].r;
g = col[counter].g;
b = col[counter].b;
average = (col[counter].r + col[counter].g + col[counter].b) /3.0f;
}
}
With r, g and b being bytes initialiazed to 0 in the Start() method, and w and h being the width and height of the image. Since GetPixels32 returns a 1D array, I used a counter to get the index, and in my case it's not important to get the pixel coordinates. There is a better way to get the index of the pixel in the array, but for my use, this (really bad) method is fine. Of course, this will take more time as the image gets bigger, but for my use, it's acceptable, I hope this can help people.
With this method, I lost around 5-8 frames per second.
This solution has an error. In line 2 only the blue channel get divided by 3. So the resulting value in "average" is much larger than the actual average. Also there are serveral simplifications you can do:
Color32[] col = tex.GetPixels32();
int maxVal = 0;
Color32 maxColor;
foreach (Color32 c in col)
{
int v = c.r + c.g + c.b;
if (v > maxVal) {
maxColor = c;
maxVal = v;
}
There's no need to divide by 3 as the relative size will be the same. Also the conversion to float is also unnecessary as we can just work with an int value. There's no byte + operator so the 3 bytes are automatically cast to int.
That's right, I haven't noticed XD Anyway, I went with another method, ins$$anonymous$$d of finding the whitest point, I compute the lu$$anonymous$$ance, and compare it, it's way more efficient this way.
Anyway, thanks for pointing out that little mistake ^^
If you use one of the conversions over here you may still use integer sums. Just muliply the channels by the corresponding integer coefficients: 299*R + 587*G + 114*B
Since R,G and B are max 255 the resulting values perfectly fit into an integer since the max value is (1000 255 == 255000) in this case. Even if you use the first option it would just be a magnitude higher (10000 255)
Follow this Question
Related Questions
Material doesn't have a color property '_Color' 4 Answers
Vertex displacement shader not working 2 Answers
Rendering only specific colors? Color filter? 1 Answer
Realistic metalic on mobile 0 Answers