2
\$\begingroup\$

I have a sphere with a shader with colors for specific heights and I want to get the value at the cursor(height) or color of the point the cursor is on when clicked. Is there a good way to get this information or the whole texture and find the value that way?

What the object looks like and its inspector. The planet

The planet's inspector

Update: I am using the below to try and capture the texture but it is just capturing black.

public class BakeMaterial : MonoBehaviour {
    public RenderTexture ResultTexture;
    public int Size = 2048;

    public Material ReplaceMaterial;
    public bool DoBake = false;
    void Update() {
        if(DoBake) {
            DoBake = false;
            Bake();
        }
    }

    // Use this for initialization
    public void Bake() {
        if(ResultTexture == null) {
            ResultTexture = new RenderTexture(Size, Size, 0);
            ResultTexture.name = "Baked Texture";
        }

        bakeTexture();

        if(ReplaceMaterial != null) {
            GetComponent<Renderer>().material = ReplaceMaterial;
            ReplaceMaterial.mainTexture = ResultTexture;
        }
    }

    void bakeTexture() {
        var renderer = GetComponent<Renderer>();
        var material = Instantiate(renderer.material);

        material.SetPass(0);

        Graphics.Blit(material.mainTexture, ResultTexture, material);

        Texture2D frame = new Texture2D(ResultTexture.width, ResultTexture.height);
        frame.ReadPixels(new Rect(0, 0, ResultTexture.width, ResultTexture.height), 0, 0, false);
        frame.Apply();
        byte[] bytes = frame.EncodeToPNG();
        FileStream file = File.Open(@"D:\Works.png", FileMode.Create);
        BinaryWriter binary = new BinaryWriter(file);
        binary.Write(bytes);
        file.Close();
    }
}

My Shader:

Shader "NanoRav/Planet-Lit" {
    Properties {

        _BumpMap("BumpMap", 2D) = "bump"{}
        _NormMap("NormMap", 2D) = "bump"{}

        _PeakColor("PeakColor", Color) = (0, 0, 0, 1)
        _Level3Color("Level3Color", Color) = (0, 0, 0, 1)
        _Level2Color("Level2Color", Color) = (0, 0, 0, 1)
        _Level1Color("Level1Color", Color) = (0, 0, 0, 1)
        _ShoreColor("ShoreColor", Color) = (0, 0, 0, 1)
        _WaterColor("WaterColor", Color) = (0, 0, 0, 1)

        _PeakLevel("PeakLevel", Float) = 0.5
        _Level3("Level3", Float) = 0.5
        _Level2("Level2", Float) = 0.5
        _Level1("Level1", Float) = 0.5
        _ShoreLevel("ShoreLevel", Float) = 0.5
        _WaterLevel("WaterLevel", Float) = 0.5

        _IceLevel("IceLevel",Float) = 0.4
        _IceStrength("IceStrength",Float) = 2.0
        _IceColor("IceColor", Color) = (0.95, 0.95, 0.95, 1)

        _Frequency("Frequency", Float) = 2.0
        _Lacunarity("Lacunarity", Float) = 3.0
        _Gain("Gain", Float) = 0.5

        _PlanetStretch("PlanetStretch",Float) = 0.4

        _AtmosNear("AtmosNear", Color) = (1,1,1,1)
        _AtmosFar("AtmosFar", Color) = (1,1,1,1)
        _AtmosFalloff("AtmosFalloff", Float) = 3

        [HideInInspector] _CityFrequency("CityFrequency", Float) = 250.0

        _CityColor("CityColor", Color) = (1.0, 1.0, 1.0, 1.0)
        [HideInInspector] _CityLevel("CityLevel",Float) = 0.7
        [HideInInspector] _isCities("isCities",Float) = 1.0

        _CloudFrequency("CloudFrequency", Float) = 2.0
        _CloudLacunarity("CloudLacunarity", Float) = 3.0
        _CloudGain("CloudGain", Float) = 0.5

        _CloudColor("CloudColor", Color) = (1.0, 1.0, 1.0, 1.0)
        _CloudLevel("CloudLevel",Float) = 0.05
        _CloudRotation("CloudRotation",Float) = 0.01
        [HideInInspector] _isClouds("isClouds",Float) = 1.0

        _CityTOP ("City Top",Float) = 0.8
        _CityBOT ("City Bot", Float) = 0.2
    }
    SubShader {

        Tags {
            "Queue"="Geometry"
            "RenderType"="Opaque"
        }

        Cull Back
        ZWrite On
        ZTest LEqual
        ColorMask RGBA
        Fog { Mode Off }

        CGPROGRAM
        #pragma surface surf BlinnPhongEditor vertex:vert
        #pragma target 3.0

        #include "UnityCG.cginc"

        uniform sampler2D _PermTable2D, _Gradient3D, _BumpMap, _NormMap;
        uniform float _PlanetStretch, _SpecPower, _PlanetType, _Frequency, _Lacunarity, _Gain, _IceStrength, _IceLevel, _Level3, _Level2, _Level1, _ShoreLevel, _WaterLevel, _PeakLevel;
        uniform float4 _ChosenColor, _IceColor, _PeakColor, _Level3Color, _Level2Color, _Level1Color, _ShoreColor, _WaterColor;

        float4 _AtmosNear;
        float4 _AtmosFar;
        float _AtmosFalloff;

        struct EditorSurfaceOutput {
            half3 Albedo;
            half3 Normal;
            half3 Emission;
            half3 Gloss;
            half Specular;
            half Alpha;
            half Custom;
          };

        inline half4 LightingBlinnPhongEditor_PrePass (EditorSurfaceOutput s, half4 light){
            half3 spec = light.a * s.Gloss;
            half4 c;
            c.rgb = (s.Albedo.rgb * light.rgb + light.rgb * spec);
            c.g -= .01 * s.Alpha;
            c.r -= .03 * s.Alpha;
            c.rg += min(s.Custom, s.Alpha);
            c.b += 5.75 * min(s.Custom, s.Alpha);
            c.b = saturate(c.b + s.Alpha * 0.02);
            c.a = 1.0;
            return c;
        }

        inline half4 LightingBlinnPhongEditor (EditorSurfaceOutput s, half3 lightDir, half3 viewDir, half atten){

            half3 h = normalize (lightDir + viewDir);
            half diff = max (0, dot ( lightDir, s.Normal ));

            float nh = max (0, dot (s.Normal, h));
            float spec = pow (nh, s.Specular*128.0);

            half4 res;
            res.rgb = _LightColor0.rgb * diff;
            res.w = spec * Luminance (_LightColor0.rgb);
            res *= atten * 1.0; //CHANGED TO 1.0 FOR 5.0

            return LightingBlinnPhongEditor_PrePass( s, res );
        }


        struct Input {
            float4 vertPos;
            float3 viewDir;
            float2 uv_BumpMap;
            float2 uv_NormMap;
        };

        float3 fade(float3 t){
            return t * t * t * (t * (t * 6 - 15) + 10);
        }

        float4 perm2d(float2 uv){
            return tex2D(_PermTable2D, uv);
        }

        float gradperm(float x, float3 p){
            float3 g = tex2D(_Gradient3D, float2(x, 0) ).rgb *2.0 - 1.0;
            return dot(g, p);
        }

        float inoise(float3 p){
            float3 P = fmod(floor(p), 256.0);   // FIND UNIT CUBE THAT CONTAINS POINT
            p -= floor(p);                      // FIND RELATIVE X,Y,Z OF POINT IN CUBE.
            float3 f = fade(p);                 // COMPUTE FADE CURVES FOR EACH OF X,Y,Z.

            P = P / 256.0;
            const float one = 1.0 / 256.0;

            // HASH COORDINATES OF THE 8 CUBE CORNERS
            float4 AA = perm2d(P.xy) + P.z;

            // AND ADD BLENDED RESULTS FROM 8 CORNERS OF CUBE
            return lerp( lerp( lerp( gradperm(AA.x, p ),
                                     gradperm(AA.z, p + float3(-1, 0, 0) ), f.x),
                               lerp( gradperm(AA.y, p + float3(0, -1, 0) ),
                                     gradperm(AA.w, p + float3(-1, -1, 0) ), f.x), f.y),

                         lerp( lerp( gradperm(AA.x+one, p + float3(0, 0, -1) ),
                                     gradperm(AA.z+one, p + float3(-1, 0, -1) ), f.x),
                               lerp( gradperm(AA.y+one, p + float3(0, -1, -1) ),
                                     gradperm(AA.w+one, p + float3(-1, -1, -1) ), f.x), f.y), f.z);
        }

        float fBm(float3 p, int octaves){
            float freq = _Frequency, amp = 0.5;
            float sum = 0;
            for(int i = 0; i < octaves; i++)
            {
                sum += inoise(p * freq) * amp;
                freq *= _Lacunarity;
                amp *= _Gain;
            }
            return sum;
        }

        void vert (inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input,o);
            o.vertPos = v.vertex;
        }

        void surf (Input IN, inout EditorSurfaceOutput  o) {

            if(_PlanetType == 6){
                IN.vertPos.xyz = IN.vertPos.xyz/float3(4,_PlanetStretch,1);
            }

            float n = fBm(IN.vertPos.xyz, 4);

                if(_Level1 < _ShoreLevel)
                    _Level1 = _ShoreLevel+0.02;

                if(_ShoreLevel < _WaterLevel)
                    _WaterLevel = _ShoreLevel-0.02;

                if(n >= _PeakLevel){
                    _ChosenColor = _PeakColor;
                }
                if(n <= _PeakLevel){
                    _ChosenColor = lerp(_Level3Color,_PeakColor, (n - _Level3)/(_PeakLevel - _Level3));
                }
                if(n <= _Level3){
                    _ChosenColor = lerp(_Level2Color,_Level3Color, (n - _Level2)/(_Level3 - _Level2));
                }
                if(n <= _Level2){
                    _ChosenColor = lerp(_Level1Color,_Level2Color, (n - _Level1)/(_Level2 - _Level1));
                }
                if(n <= _Level1){
                    _ChosenColor = lerp(_ShoreColor,_Level1Color, (n - _ShoreLevel)/(_Level1 - _ShoreLevel));
                }
                if(n <= _ShoreLevel){
                    _ChosenColor = lerp(_WaterColor,_ShoreColor, (n - _WaterLevel)/(_ShoreLevel - _WaterLevel));
                }
                if (n <= _WaterLevel){
                    _ChosenColor = _WaterColor;
                }


                if (_PlanetType == 0 && n >= _Level1 && IN.vertPos.y >= _IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_ChosenColor*_IceStrength, (IN.vertPos.y - _IceLevel)/(_IceLevel));

                if (_PlanetType == 0 && n >= _Level1 && IN.vertPos.y <= -_IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_ChosenColor*_IceStrength, (IN.vertPos.y + _IceLevel)/(-_IceLevel));

                if (_PlanetType == 2 && IN.vertPos.y >= _IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_ChosenColor*_IceStrength, (IN.vertPos.y - _IceLevel)/(_IceLevel));

                if (_PlanetType == 2 && IN.vertPos.y <= -_IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_ChosenColor*_IceStrength, (IN.vertPos.y + _IceLevel)/(-_IceLevel));

                if (_PlanetType == 3 && n >= _Level1 && IN.vertPos.y >= _IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_IceColor, (IN.vertPos.y - _IceLevel)/(_IceLevel));

                if (_PlanetType == 3 && n >= _Level1 && IN.vertPos.y <= -_IceLevel)
                    _ChosenColor = lerp(_ChosenColor,_IceColor, (IN.vertPos.y + _IceLevel)/(-_IceLevel));

            o.Custom = 0.0;

            float4 Fresnel0_1_NoInput = float4(0,0,1,1);
            float4 Fresnel0=(1.0 - dot( normalize( float4( IN.viewDir.x, IN.viewDir.y,IN.viewDir.z,1.0 ).xyz), normalize( Fresnel0_1_NoInput.xyz ) )).xxxx;
            float4 Pow0=pow(Fresnel0,_AtmosFalloff.xxxx);
            float4 Saturate0=saturate(Pow0);
            float4 Lerp0=lerp(_AtmosNear,_AtmosFar,Saturate0);
            float4 Multiply1=Lerp0 * Saturate0;
            float4 Add0=Multiply1 + _ChosenColor;

            o.Albedo = Add0.xyz;
            o.Specular = 1.0;

            o.Gloss = 0.5;
            o.Normal = UnpackNormal(tex2D (_NormMap, IN.uv_NormMap));

            if(n > _ShoreLevel){
                o.Gloss = 0.1;
                o.Normal = UnpackNormal(tex2D (_BumpMap, IN.uv_BumpMap));
            }else if(_PlanetType == 2){
                o.Gloss = 0.1;
                o.Normal = UnpackNormal(tex2D (_BumpMap, IN.uv_BumpMap));
            }else if(_PlanetType == 5){
                o.Gloss = 0.1;
                o.Normal = UnpackNormal(tex2D (_BumpMap, IN.uv_BumpMap));
            }

            if(_PlanetType == 6){
                o.Gloss = 0.1;
                o.Normal = UnpackNormal(tex2D (_NormMap, IN.uv_NormMap));
            }

            if(_PlanetType == 0){
                if(n > _ShoreLevel){
                    o.Gloss = 0.01;
                }
                o.Gloss = 0.05;
            }

        }
        ENDCG

    }
}
\$\endgroup\$
  • \$\begingroup\$ It could be helpful to elaborate on "using perlin noise with colors for specific heights". Clarity is very helpful on this site, and if this information is relevant to the problem, it is unclear (to me, at least) what exactly you want to achieve. \$\endgroup\$ – Jibb Smart May 5 '16 at 3:43
  • \$\begingroup\$ When you say "heights" does that mean the sphere is being distorted, like in displacement mapping? \$\endgroup\$ – DMGregory May 5 '16 at 3:49
  • 1
    \$\begingroup\$ I edited the question to hopefully to make more sense. @DMGregory, Height is a bad way of me describing it I am computing the height through perlin noise and using that value for deciding the color to use. I guess what I am really trying to ask is there a way to capture that value within the shader and make it available in unity? \$\endgroup\$ – Earthmonkey0 May 5 '16 at 3:55
2
\$\begingroup\$

The cheapest (but not necessarily very flexible) solution would be to duplicate the shader code in your C# script.

When the player clicks, fire a ray through the mouse position. Capture the RaycastHit struct, and it will give you the world position and texture coordinates of the point directly under the mouse.

(If you've displaced your sphere out along the normals, mapping the raycast hit to the actual visibly struck position is non-trivial - fortunately it sounds like that doesn't apply in this case.)

Dump those parameters into your C# version of the shader code, and you can compute the colours identically to what's displayed on the model, without any expensive texture read-back.

The problem with this method is if you change your shader or its material paraeters, you need to remember to make a corresponding change to your C# script, or their results will diverge. This is rather brittle.

The bluntest way to do it would be to render either your full scene, or the mesh's surface, to a texture anytime it changes. Then you can use GetPixels to copy the contents of that texture so they're accessible to your C# script, and lookup the colour at the clicked point in the resulting colour buffer. This will be less code, and is flexible to shader changes, but it has a significant performance cost to consider.

It's past midnight here so I won't go into the full details just now. The key items you need to know are:

  • Creating RenderTexture and setting it as the current target with RenderTexture.active

  • Create a pass or version of your planet material where you replace the transformed vertex coordinates withfloat4(i.uv * 2.0f - 1.0f, 0.0f, 1.0f); so it outputs a texture map for the object instead of its 3D shape. You might need to flip the y direction and/or change the culling mode to make this work - I can never remember which way's right so I do these by trial and error. ;)

  • planetMaterial.SetPass(passID); to bind a particular shader effect

  • Graphics.DrawMeshNow to render into your RenderTexture

  • Texture2D.ReadPixels to read the current RenderTarget into a Texture2D object you've created (this feels redundant, but it's the only way I know to do readback)

  • Texture2D.GetPixel to look up a pixel colour in this texture, or GetPixels32 to copy the whole buffer into an array for bulk/repeated use.

\$\endgroup\$
  • \$\begingroup\$ I don't plan on these changing a lot, so with the solution of rendering to texture and caching it so I can do lookups sounds good. I am still pretty new to unity, how would I go about capturing the texture and do a look up of a color? \$\endgroup\$ – Earthmonkey0 May 5 '16 at 3:59
  • \$\begingroup\$ Added additional information above, I am not able to capture the texture from material. \$\endgroup\$ – Earthmonkey0 May 6 '16 at 5:55
  • \$\begingroup\$ What made you choose Graphics.Blit instead of DrawMeshNow as described above? \$\endgroup\$ – DMGregory May 6 '16 at 11:50
  • \$\begingroup\$ I tried using Draw Mesh now and didnt see it working, with Blit I was able to at least get something but it is black. I also didn't see how to set the render texture as active and with blit I can write to it directly. \$\endgroup\$ – Earthmonkey0 May 6 '16 at 15:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.