Search Unity

How do I create a new Texture2D inside the code of a Shader

Discussion in 'Shaders' started by TylerBetanski, Dec 24, 2020.

  1. TylerBetanski

    TylerBetanski

    Joined:
    Dec 18, 2020
    Posts:
    3
    So I'm using the Standard Surface Shader, and I'm trying to procedurally generate a smoothness and metallic map while the shader runs. Every time I try to create a line like this:
    Code (CSharp):
    1. Texture2D smoothTex = new Texture2D(32, 32);
    (For context this is inside of the surf method)​
    I get an error in the console saying:
    " Shader error in 'Custom/Reveal Shader': Unexpected identifier "smoothTex". Expected one of ',' ';' at line 50
    Am I just creating the texture wrong, or is it even possible to make a texture inside of the shader?
    And if it is possible to create the texture, how would I edit it inside of the shader, based on the world position.

    Also, how would I get the width and height of a pre existing texture, like the _MainTex? I assume it would be something like:
    Code (CSharp):
    1. _MainTex.width;
    or
    Code (CSharp):
    1. _MainTex.getWidth();
    Any help would be greatly appreciated, and I've posted the full code below if you need to see more of it.

    Code (CSharp):
    1. Shader "Custom/Reveal Shader"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1,1,1,1)
    6.         _MainTex ("Albedo (RGB)", 2D) = "white" {}
    7.         _Glossiness ("Smoothness", Range(0,1)) = 0.5
    8.         _Metallic ("Metallic", Range(0,1)) = 0.0
    9.     }
    10.     SubShader
    11.     {
    12.         Tags { "RenderType"="Opaque" }
    13.         LOD 200
    14.  
    15.         CGPROGRAM
    16.         #pragma surface surf Standard fullforwardshadows
    17.  
    18.         #pragma target 3.0
    19.  
    20.         sampler2D _MainTex;
    21.  
    22.         struct Input
    23.         {
    24.             float2 uv_MainTex;
    25.             float3 worldPos : TEXCOORD2;
    26.             float3 viewDir;
    27.         };
    28.  
    29.         half _Glossiness;
    30.         half _Metallic;
    31.         fixed4 _Color;
    32.  
    33.         void vert(inout appdata_full v, out Input o) {
    34.             UNITY_INITIALIZE_OUTPUT(Input, o);
    35.             o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    36.         }
    37.  
    38.         uniform float4 _arrayPosition[1000];
    39.         uniform float _arrayStrength[1000];
    40.         int _arrayLength;
    41.  
    42.         UNITY_INSTANCING_BUFFER_START(Props)
    43.         UNITY_INSTANCING_BUFFER_END(Props)
    44.  
    45.         void surf (Input IN, inout SurfaceOutputStandard o)
    46.         {
    47.             fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    48.  
    49.             float4 color = float4(0, 0, 0, 0);
    50.             Texture2D smoothTex = new Texture2D(32, 32);
    51.  
    52.             for (int i = 0; i < _arrayLength; i++) {
    53.                 float dist = distance(IN.worldPos, float4(_arrayPosition[i].xyz, 1.0));
    54.  
    55.                     if (dist < 10)
    56.                     {
    57.                         o.Albedo = c.rgb;
    58.                         o.Metallic = _Metallic;
    59.                         o.Smoothness = _Glossiness;
    60.                         o.Alpha = c.a;
    61.                     }
    62.             }
    63.             o.Metallic = 0;
    64.             o.Smoothness = 1;
    65.         }
    66.         ENDCG
    67.     }
    68.     FallBack "Diffuse"
    69. }
    70.  
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Vertex fragment shaders (of which the Surface Shader above turns into) can’t create anything. They only take input data, do some match, and output a result in the form of a color value. It’s run once for every pixel of geometry visible using that shader. That’s it. If you want it to write to a texture, you can, but you have to create a render texture in c# code, set it as the current render target, and then draw the mesh you want it drawn with to that render target with that shader.

    If you want to know the resolution of a texture that the shader is reading from, you have to pass that information to the shader from c#. Technically Unity does this already if you add
    float4 _TextureName_TexelSize;
    to your shader with the same name as the texture. You can search for “Unity TexelSize” to find out what the individual components mean.

    If you want to read from and write to the same texture ... you can’t.* You’re either reading from a texture, or writing to one, but not both at the same time. So generally you have to have another render texture that you’re rendering to. And if you need to know the resolution of that, you absolutely need to pass that information to your shader manually via c# using properties on the material or shader globals.

    Really, I would suggest describing the end goal you’re trying to accomplish and we can help point you in the right direction for how to go about doing that.

    * Some platforms allow you to read from and write to the same render target, specifically Apple’s Metal, and some Android devices. You can also set a render texture to be a random write target, and then read from and write to it, but then you still need another render texture that you’re rendering too that’s the same resolution to invoke the rendering.
     
  3. TylerBetanski

    TylerBetanski

    Joined:
    Dec 18, 2020
    Posts:
    3
    Alright, I think I understand, thanks for the info.

    I've been working on making a shader that takes an array of points and colors the world in a radius around those points, leaving the rest of the world black. I've got it all set up and working properly, but I've noticed that the smoothness and metallic affects every part of the material the same, which means that I either have to make everything 100% smooth, or else there will be some reflections in the black parts of the world.

    I suppose I can eventually create metallic and smoothness maps for my assets, but at this stage I'm just working on creating the rough effect.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The easiest solution is don’t use a Surface Shader. Or if you still want shading, use Lambert, or Standard Specular shading model. Lambert has no specular or reflections, and Standard Specular you can use a black specular color for the black areas which will disable specular and reflections too.
    https://docs.unity3d.com/Manual/SL-SurfaceShaderLightingExamples.html
     
  5. TylerBetanski

    TylerBetanski

    Joined:
    Dec 18, 2020
    Posts:
    3
    Alright, thanks for all the help.