I have a stone texture that looks great, except for one thing: it's very dark and I want white stone.
I don't want to mess with the texture, because it's used for other things in the same project. What I'd like to do is invert the brightness in a shader. Specifically, I want a custom shader that does everything exactly the same way as the Standard PBR shader, except that it inverts brightness.
By "inverting brightness", I mean:
- take albedo.RGB, convert to HSV
- V of HSV = 1 - V
- Convert back to RGB
I tried making a trivial custom shader that does exactly this, but it has two problems:
- The result is completely flat, because the custom shader template didn't include the normal map and the height map.
- The brightness didn't actually get inverted!
Not sure what's going on with point #2, but when I tried adding the additional textures to the custom shader, I discovered that the SurfaceOutputStandard struct doesn't have a field for outputting height map data to!
Does anyone know how to make this work? Here's what I've got so far:
Shader "Custom/Bright Stone"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_NormalTex ("Normal Map", 2D) = "white" {}
_HeightTex("Height Map", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _NormalTex;
sampler2D _HeightTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
float3 rgb_to_hsv_no_clip(float3 RGB)
{
float3 HSV;
float minChannel, maxChannel;
if (RGB.x > RGB.y) {
maxChannel = RGB.x;
minChannel = RGB.y;
}
else {
maxChannel = RGB.y;
minChannel = RGB.x;
}
if (RGB.z > maxChannel) maxChannel = RGB.z;
if (RGB.z < minChannel) minChannel = RGB.z;
HSV.xy = 0;
HSV.z = maxChannel;
float delta = maxChannel - minChannel; //Delta RGB value
if (delta != 0) { // If gray, leave H S at zero
HSV.y = delta / HSV.z;
float3 delRGB;
delRGB = (HSV.zzz - RGB + 3 * delta) / (6.0*delta);
if (RGB.x == HSV.z) HSV.x = delRGB.z - delRGB.y;
else if (RGB.y == HSV.z) HSV.x = (1.0 / 3.0) + delRGB.x - delRGB.z;
else if (RGB.z == HSV.z) HSV.x = (2.0 / 3.0) + delRGB.y - delRGB.x;
}
return (HSV);
}
float3 hsv_to_rgb(float3 HSV)
{
float3 RGB = HSV.z;
float var_h = HSV.x * 6;
float var_i = floor(var_h); // Or ... var_i = floor( var_h )
float var_1 = HSV.z * (1.0 - HSV.y);
float var_2 = HSV.z * (1.0 - HSV.y * (var_h - var_i));
float var_3 = HSV.z * (1.0 - HSV.y * (1 - (var_h - var_i)));
if (var_i == 0) { RGB = float3(HSV.z, var_3, var_1); }
else if (var_i == 1) { RGB = float3(var_2, HSV.z, var_1); }
else if (var_i == 2) { RGB = float3(var_1, HSV.z, var_3); }
else if (var_i == 3) { RGB = float3(var_1, var_2, HSV.z); }
else if (var_i == 4) { RGB = float3(var_3, var_1, HSV.z); }
else { RGB = float3(HSV.z, var_1, var_2); }
return (RGB);
}
float3 InvertBrightness(float3 albedo)
{
float3 hsv = rgb_to_hsv_no_clip(albedo);
hsv.z = 1.0 - hsv.z;
return hsv_to_rgb(hsv);
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = InvertBrightness(c.rgb);
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
o.Normal = tex2D(_NormalTex, IN.uv_MainTex);
// o.Height = tex2D(_HeightTex, IN.uv_MainTex);
}
ENDCG
}
FallBack "Diffuse"
}
If I'm going about this completely wrong and there's a better way to do it, feel free to point that out as well.