Search Unity

Need help saving Shader render output to texture, Graphics.Blit is being difficult

Discussion in 'General Graphics' started by TheProfessor, Dec 31, 2020.

  1. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    I am trying to acquire Perlin Noise (FBM) in real time to use for a height map, I want to output the results of the shader from here and save it to a texture, but the result isn't really working.

    What's happening is that it seems like whatever blit is saving is the wrong dimensions/uvs compared to the plane the material is actually applied to:
    here is what the material gives. here is the result I get using Blit.

    Any help would be appreciate for the best way to achieve what I want. Ideally I want to be able to save any arbitrary resolution to a texture with the appropriate output from the shader. i.e 512 by 512 should be cropped to the corner compared to the preview, etc.
     
  2. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    I do this to render material to texture and it's been working fine for me, maybe there's some part of this you don't have or have done differently?
    Code (CSharp):
    1. RenderTexture renderTexture = RenderTexture.GetTemporary(resolution.x, resolution.y);
    2. Graphics.Blit(null, renderTexture, material);
    3. Texture2D texture = new Texture2D(resolution.x, resolution.y, TextureFormat.RGBA32, false);
    4. texture.filterMode = FilterMode.Bilinear;
    5. texture.wrapMode = TextureWrapMode.Clamp;
    6. RenderTexture.active = renderTexture;
    7. texture.ReadPixels(new Rect(Vector2.zero, resolution), 0, 0);
    8. RenderTexture.active = null;
    9. RenderTexture.ReleaseTemporary(renderTexture);
    10. // ..call Destroy on reference to [I]texture[/I] when you don't need it anymore,
    11. // ..you need to call texture.Apply() when you modify it to apply your changes,
    12. // ..ReadPixels, "If recalculateMipMaps is set to false, you must call Apply to recalculate them:
    13. // https://docs.unity3d.com/ScriptReference/Texture2D.ReadPixels.html
    Hopefully the above is enough to get things working :)

    Edit: /code tag was in wrong place, broke other tags
     
  3. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    I'll take a look but here's my pastebin, I believe I am doing everything right, but the resolution/ubs looks wrong: https://pastebin.com/GvstNp4Y

    Code (csharp):
    1.  
    2. [LIST=1]
    3. [*]        public void Save()
    4. [*]        {
    5. [*]          
    6. [*]          
    7. [*]            RenderTexture buffer = new RenderTexture(
    8. [*]                                   TextureLength,
    9. [*]                                   TextureLength,
    10. [*]                                   0,                            // No depth/stencil buffer
    11. [*]                                   RenderTextureFormat.ARGB32,   // Standard colour format
    12. [*]                                   RenderTextureReadWrite.Linear
    13. [*]                                   );
    14. [*]                                        /*
    15. [*]                            RenderTexture buffer = new RenderTexture(
    16. [*]                                        TextureLength,
    17. [*]                                        TextureLength,
    18. [*]                                        0,                            // No depth/stencil buffer
    19. [*]                                        RenderTextureFormat.ARGB32,   // Standard colour format
    20. [*]                                        RenderTextureReadWrite.sRGB // No sRGB conversions
    21. [*]                                    );
    22. [*]                                    */
    23. [*]            buffer.DiscardContents();
    24. [*]
    25.  
    26. [*]            texture = new Texture2D(TextureLength, TextureLength, TextureFormat.ARGB32, true);
    27. [*]
    28.  
    29. [*]            //MeshRenderer render = GetComponent<MeshRenderer>();
    30. [*]            //texture = render.sharedMaterial.GetTexture("_MainTex") as Texture2D;
    31. [*]            Material material = m_renderer.material;
    32. [*]
    33.  
    34. [*]            Graphics.Blit(null, buffer, material);
    35. [*]            RenderTexture.active = buffer;           // If not using a scene camera
    36. [*]
    37.  
    38. [*]            texture.ReadPixels(
    39. [*]              new Rect(0, 0, TextureLength, TextureLength), // Capture the whole texture
    40. [*]              0, 0,                          // Write starting at the top-left texel
    41. [*]              false);                          // No mipmaps
    42. [*]            texture.Apply();
    43. [*]            RenderTexture.active = null;
    44. [*]            buffer.Release();
    45. [*]            byte[] bytes = texture.EncodeToPNG();
    46. [*]            File.WriteAllBytes(Application.dataPath + "/Temp/test.png", bytes);
    47. [*]            // texture.Save();
    48. [*]
    49.  
    50. [*]        }
    51. [*]
    [/LIST]
     
  4. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Yeah using your code gives me the same results as before. Something is wrong I think with how the shader outputs its information.
     
  5. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    :( okay, well that's all I can think of right now to mention. If anything else comes to mind I'll post again.
     
  6. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Another thing I'd like to mention is that it seems like Blit is only capturing 1 dimension of the shader output, i.e only the X or U but not both XY or UV.
     
  7. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    It seems that you are trying to Blit() with shader, intended to render geomerty. It passes vertex position as uv:
    Code (CSharp):
    1.             struct v2f
    2.             {
    3.                 float4 pos : SV_POSITION;
    4.                 float4 uv : TEXCOORD;
    5.             };
    6.        
    7.             v2f vert (appdata_base v)
    8.             {
    9.                 v2f o;
    10.                 o.pos = UnityObjectToClipPos(v.vertex);
    11.                 o.uv = v.vertex;
    12.                 return o;
    13.             }
    And then uses uv.xz in fragment program to calculate stuff:
    Code (CSharp):
    1.                 if (_NoiseStyle == 0)
    2.                 {
    3.                     n = fBm(i.uv.xz, 4);
    4.                 }
    5.                 else if (_NoiseStyle == 1)
    6.                 {
    7.                     n = turbulence(i.uv.xz, 4);
    8.                 }
    9.                 else if (_NoiseStyle == 2)
    10.                 {
    11.                     n = ridgedmf(i.uv.xz, 4, 1.0);
    12.                 }
    13.    
    14.                 return half4(n,n,n,1);
    Blit() doesn't know anything about your plane the material is applied to. Internally it draws procedural full-screen quad consisting of 4 vertices, with X and Y position in [0,1] range and constant Z value. As a result, the shader outputs noise along a single axis in limited range.
     
    Last edited: Jan 1, 2021
  8. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Is there a way to go about fixing that?

    Alternatively, if I go the take a texture of the *Camera's* view instead, how can I adjust the shader to set the uv's according to the plane's scale? i.e if the plane is 2x wider, for it to just display what it should display instead of squashing or stretching it?
     
  9. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    As far as I understand, this shader is intended to sample a plane along X and Z axes in object space, with centered pivot I guess. So If it has a size of 10x10 units, it goes from -5 to 5 in each dimension. Blit() positions go from 0 to 1, so you need to do something like this:
    Code (CSharp):
    1.  
    2. float scale = 10.0;
    3. float2 coords = scale * (i.uv.xy  - 0.5);  //assuming i.uv contains vertex position
    4.  
    Then replace i.uv.xz with these new coords in noise functions. I think It should not be stretched, if you render into square texture. Sorry, making it up on the fly, no time to test.
     
  10. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Yeah something like that helped:


    Code (CSharp):
    1.                 o.uv = v.texcoord - float4(0.5, 0.5, 0, 0);
    2.                 o.uv.x = o.uv.x * _Scale.x; // for tiling
    3.                 o.uv.y = o.uv.y * _Scale.y; // for tiling
    So that part is now solved. My issue now is I'd like to be able to both scale and offset the final result. Currently the - float4(0.5....) centers it for scaling, but if I try to do o.uv + _Offset it no longer centers it when zooming in and out.

    Any ideas?
     
  11. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Idk, if you do scale before offset it should work. I made a quick test, no such problem:



    The shader code:
    Code (CSharp):
    1. Shader "CustomRP/TestBlit"
    2. {
    3.     Properties
    4.     {
    5.         [NoScaleOffset] _MainTex("Texture", 2D) = "white" {}
    6.         _Scale ("Scale", Vector) = (1, 1, 0, 0)
    7.         _Offset("Offset", Vector) = (0, 0, 0, 0)
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Cull Off ZWrite Off
    13.  
    14.         Pass
    15.         {
    16.             HLSLPROGRAM
    17.             #pragma target 3.5
    18.             #pragma vertex Vert
    19.             #pragma fragment Frag
    20.  
    21.             #include "Assets/Custom RP/Shader Library/Common.hlsl"
    22.  
    23.             TEXTURE2D(_MainTex);
    24.             SAMPLER(sampler_linear_repeat);
    25.  
    26.             struct Attributes
    27.             {
    28.                 float3 positionOS : POSITION;
    29.                 float2 uv : TEXCOORD0;
    30.             };
    31.  
    32.             struct Varyings
    33.             {
    34.                 float4 positionCS : SV_POSITION;
    35.                 float2 uv : VAR_UV;
    36.             };
    37.  
    38.             float2 _Scale;
    39.             float2 _Offset;
    40.             Varyings Vert(Attributes input)
    41.             {
    42.                 Varyings output;
    43.                 output.positionCS = TransformObjectToHClip(input.positionOS);
    44.                 output.uv = (input.uv - 0.5) * _Scale + _Offset;
    45.                 return output;
    46.             }
    47.  
    48.             float4 Frag(Varyings input) : SV_Target
    49.             {
    50.                 float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_linear_repeat, input.uv);
    51.                 return color;
    52.             }        
    53.  
    54.             ENDHLSL
    55.         }
    56.     }
    57. }
    58.  
    Only I do Blit() into camera target, instead of a square texture. So it gets stretched along x, should multiply it by aspect ratio. But I think you don't need this in your setup.
     
  12. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Unfortunately that isn't the scaling I mean, in the context of the GPU Perlin Noise I mean the *Frequency* for the noise, not the X/Y tiling "scaling"; i.e the ability to zoom in and out while still being centered.
     
  13. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    I meant the same thing. Only I used some default texture for demonstration, instead of procedural noise (as I don't have it). How exactly do you sample the noise? Better post your version of the shader code, otherwise it's not clear what's going on.
     
    Last edited: Jan 1, 2021
  14. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    In the video you provided I didn't see you zoom in and out though? I saw you affect the tiling but I didn't see the "camera" pull away/go closer?

    For context here is the original code:


    Code (CSharp):
    1.  
    2. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    3.  
    4.  
    5.  
    6. Shader "Noise/ImprovedPerlinNoise2D"
    7. {
    8.     SubShader
    9.     {
    10.         Pass
    11.         {
    12.    
    13.             CGPROGRAM
    14.  
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #pragma target 3.0
    18.             #include "UnityCG.cginc"
    19.            
    20.             sampler2D _PermTable1D, _Gradient2D;
    21.             float _Frequency, _Lacunarity, _Gain;
    22.             float _NoiseStyle;
    23.            
    24.             struct v2f
    25.             {
    26.                 float4 pos : SV_POSITION;
    27.                 float4 uv : TEXCOORD;
    28.             };
    29.            
    30.             v2f vert (appdata_base v)
    31.             {
    32.                 v2f o;
    33.                 o.pos = UnityObjectToClipPos(v.vertex);
    34.                 o.uv = v.vertex;
    35.                 return o;
    36.             }
    37.            
    38.             float2 fade(float2 t)
    39.             {
    40.                 return t * t * t * (t * (t * 6 - 15) + 10);
    41.             }
    42.            
    43.             float perm(float x)
    44.             {
    45.                 return tex2D(_PermTable1D, float2(x,0)).a;
    46.             }
    47.            
    48.             float grad(float x, float2 p)
    49.             {
    50.                 float2 g = tex2D(_Gradient2D, float2(x*8.0, 0) ).rg *2.0 - 1.0;
    51.                 return dot(g, p);
    52.             }
    53.                        
    54.             float inoise(float2 p)
    55.             {
    56.                 float2 P = fmod(floor(p), 256.0);    // FIND UNIT SQUARE THAT CONTAINS POINT
    57.                   p -= floor(p);                      // FIND RELATIVE X,Y OF POINT IN SQUARE.
    58.                 float2 f = fade(p);                 // COMPUTE FADE CURVES FOR EACH OF X,Y.
    59.            
    60.                 P = P / 256.0;
    61.                 const float one = 1.0 / 256.0;
    62.                
    63.                 // HASH COORDINATES OF THE 4 SQUARE CORNERS
    64.                   float A = perm(P.x) + P.y;
    65.                   float B = perm(P.x + one) + P.y;
    66.            
    67.                 // AND ADD BLENDED RESULTS FROM 4 CORNERS OF SQUARE
    68.                   return lerp( lerp( grad(perm(A    ), p ),
    69.                                    grad(perm(B    ), p + float2(-1, 0) ), f.x),
    70.                              lerp( grad(perm(A+one    ), p + float2(0, -1) ),
    71.                                    grad(perm(B+one    ), p + float2(-1, -1) ), f.x), f.y);
    72.                                      
    73.             }
    74.            
    75.             // fractal sum, range -1.0 - 1.0
    76.             float fBm(float2 p, int octaves)
    77.             {
    78.                 float freq = _Frequency, amp = 0.5;
    79.                 float sum = 0;  
    80.                 for(int i = 0; i < octaves; i++)
    81.                 {
    82.                     sum += inoise(p * freq) * amp;
    83.                     freq *= _Lacunarity;
    84.                     amp *= _Gain;
    85.                 }
    86.                 return sum;
    87.             }
    88.            
    89.             // fractal abs sum, range 0.0 - 1.0
    90.             float turbulence(float2 p, int octaves)
    91.             {
    92.                 float sum = 0;
    93.                 float freq = _Frequency, amp = 1.0;
    94.                 for(int i = 0; i < octaves; i++)
    95.                 {
    96.                     sum += abs(inoise(p*freq))*amp;
    97.                     freq *= _Lacunarity;
    98.                     amp *= _Gain;
    99.                 }
    100.                 return sum;
    101.             }
    102.            
    103.             // Ridged multifractal, range 0.0 - 1.0
    104.             // See "Texturing & Modeling, A Procedural Approach", Chapter 12
    105.             float ridge(float h, float offset)
    106.             {
    107.                 h = abs(h);
    108.                 h = offset - h;
    109.                 h = h * h;
    110.                 return h;
    111.             }
    112.            
    113.             float ridgedmf(float2 p, int octaves, float offset)
    114.             {
    115.                 float sum = 0;
    116.                 float freq = _Frequency, amp = 0.5;
    117.                 float prev = 1.0;
    118.                 for(int i = 0; i < octaves; i++)
    119.                 {
    120.                     float n = ridge(inoise(p*freq), offset);
    121.                     sum += n*amp*prev;
    122.                     prev = n;
    123.                     freq *= _Lacunarity;
    124.                     amp *= _Gain;
    125.                 }
    126.                 return sum;
    127.             }
    128.            
    129.             half4 frag (v2f i) : COLOR
    130.             {
    131.                 float n = 0;
    132.  
    133.                 //I reconmend not to actually use the conditional statment.
    134.                 //Just pick one stlye or have a seperate shader for each.
    135.                 if (_NoiseStyle == 0)
    136.                 {
    137.                     n = fBm(i.uv.xz, 4);
    138.                 }
    139.                 else if (_NoiseStyle == 1)
    140.                 {
    141.                     n = turbulence(i.uv.xz, 4);
    142.                 }
    143.                 else if (_NoiseStyle == 2)
    144.                 {
    145.                     n = ridgedmf(i.uv.xz, 4, 1.0);
    146.                 }
    147.            
    148.                 return half4(n,n,n,1);
    149.             }
    150.            
    151.             ENDCG
    152.    
    153.         }
    154.     }
    155.     Fallback "VertexLit"
    156. }

    Here's mine so far:

    Code (CSharp):
    1.  
    2. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
    3.  
    4.  
    5.  
    6. Shader "Noise/ImprovedPerlinNoise2D"
    7. {
    8.     Properties
    9.     {
    10.         _MainTex("Texture", 2D) = "white" {}
    11.     }
    12.     SubShader
    13.     {
    14.         Pass
    15.         {
    16.  
    17.             CGPROGRAM
    18.  
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #pragma target 3.0
    22.             #include "UnityCG.cginc"
    23.  
    24.             sampler2D _PermTable1D, _Gradient2D;
    25.             float _Frequency, _Lacunarity, _Gain;
    26.             float _NoiseStyle;
    27.             float2 _Scale;
    28.             float2 _Offset;
    29.  
    30.             struct v2f
    31.             {
    32.                 float4 pos : SV_POSITION;
    33.                 float4 uv : TEXCOORD;
    34.             };
    35.  
    36.             sampler2D _MainTex;
    37.             float4 _MainTex_ST;
    38.  
    39.             v2f vert(appdata_base v)
    40.             {
    41.                 v2f o;
    42.                 o.pos = UnityObjectToClipPos(v.vertex);
    43.  
    44.                 o.uv = v.texcoord - float4(0.5, 0.5, 0, 0);
    45.                 o.uv.x = o.uv.x * _Scale.x;
    46.                 o.uv.y = o.uv.y * _Scale.y;
    47.  
    48.                 return o;
    49.             }
    50.  
    51.             float2 fade(float2 t)
    52.             {
    53.                 return t * t * t * (t * (t * 6 - 15) + 10);
    54.             }
    55.  
    56.             float perm(float x)
    57.             {
    58.                 return tex2D(_PermTable1D, float2(x,0)).a;
    59.             }
    60.  
    61.             float grad(float x, float2 p)
    62.             {
    63.                 float2 g = tex2D(_Gradient2D, float2(x*8.0, 0)).rg *2.0 - 1.0;
    64.                 return dot(g, p);
    65.             }
    66.  
    67.             float inoise(float2 p)
    68.             {
    69.                 //p += _Offset; // adds panning but results in parallax
    70.                 float2 P = fmod(floor(p), 256.0);    // FIND UNIT SQUARE THAT CONTAINS POINT
    71.                 p -= floor(p);                      // FIND RELATIVE X,Y OF POINT IN SQUARE.
    72.                 float2 f = fade(p);                 // COMPUTE FADE CURVES FOR EACH OF X,Y.
    73.  
    74.                 P = P / 256.0;
    75.                 const float one = 1.0 / 256.0;
    76.  
    77.                 // HASH COORDINATES OF THE 4 SQUARE CORNERS
    78.                 float A = perm(P.x) + P.y;
    79.                 float B = perm(P.x + one) + P.y;
    80.  
    81.                 // AND ADD BLENDED RESULTS FROM 4 CORNERS OF SQUARE
    82.                 return lerp(lerp(grad(perm(A), p),
    83.                                    grad(perm(B), p + float2(-1, 0)), f.x),
    84.                              lerp(grad(perm(A + one), p + float2(0, -1)),
    85.                                    grad(perm(B + one), p + float2(-1, -1)), f.x), f.y);
    86.  
    87.             }
    88.  
    89.             // fractal sum, range -1.0 - 1.0
    90.             float fBm(float2 p, int octaves)
    91.             {
    92.                 float freq = _Frequency, amp = 0.5;
    93.                 float sum = 0;
    94.                 for (int i = 0; i < octaves; i++)
    95.                 {
    96.                     sum += inoise(p * freq) * amp;
    97.                     freq *= _Lacunarity;
    98.                     amp *= _Gain;
    99.                 }
    100.                 return sum;
    101.             }
    102.  
    103.             // fractal abs sum, range 0.0 - 1.0
    104.             float turbulence(float2 p, int octaves)
    105.             {              
    106.                 float sum = 0;
    107.                 float freq = _Frequency, amp = 1.0;
    108.                 for (int i = 0; i < octaves; i++)
    109.                 {
    110.                     sum += abs(inoise(p * freq))*amp;
    111.                     freq *= _Lacunarity;
    112.                     amp *= _Gain;
    113.                 }
    114.                 return sum;
    115.             }
    116.  
    117.             // Ridged multifractal, range 0.0 - 1.0
    118.             // See "Texturing & Modeling, A Procedural Approach", Chapter 12
    119.             float ridge(float h, float offset)
    120.             {
    121.                 h = abs(h);
    122.                 h = offset - h;
    123.                 h = h * h;
    124.                 return h;
    125.             }
    126.  
    127.             float ridgedmf(float2 p, int octaves, float offset)
    128.             {
    129.                 float sum = 0;
    130.                 float freq = _Frequency, amp = 0.5;
    131.                 float prev = 1.0;
    132.                 for (int i = 0; i < octaves; i++)
    133.                 {
    134.                     float n = ridge(inoise(p*freq), offset);
    135.                     sum += n * amp*prev;
    136.                     prev = n;
    137.                     freq *= _Lacunarity;
    138.                     amp *= _Gain;
    139.                 }
    140.                 return sum;
    141.             }
    142.  
    143.             half4 frag(v2f i) : COLOR
    144.             {
    145.                 float n = 0;          
    146.  
    147.                 //I reconmend not to actually use the conditional statment.
    148.                 //Just pick one stlye or have a seperate shader for each.
    149.                 if (_NoiseStyle == 0)
    150.                 {
    151.                     n = fBm(i.uv, 4);
    152.                 }
    153.                 else if (_NoiseStyle == 1)
    154.                 {
    155.                     n = turbulence(i.uv, 4);
    156.                 }
    157.                 else if (_NoiseStyle == 2)
    158.                 {
    159.                     n = ridgedmf(i.uv, 4, 1.0);
    160.                 }
    161.  
    162.                 return half4(n,n,n,1);
    163.             }
    164.  
    165.             ENDCG
    166.  
    167.         }
    168.     }
    169.     Fallback "VertexLit"
    170. }
     
  15. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    May be it looks like tiling because the texture is repeating itself, but actually it is sampling deeper in chosen direction, which technically is increasing frequency. I copy-pasted noise function from somewhere, is this what you mean by zoom in/out?



    Why the offset is inside one of the noise functions? Its supposed to be applied to uv in vert()
     
    Last edited: Jan 1, 2021
  16. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74

    Because I couldn't get it to work any other way.

    If I try to replicate your example (which is what I am looking for) using:

    Code (CSharp):
    1. o.uv = (v.texcoord - 0.5) * float4(_Scale, 0, 0) + float4(_Offset, 0, 0);
    It doesn't work. The "zoom point" shifts in the direction of the offset.

    Can you provide what you're full code is including how you're including the noise?
     
    Last edited: Jan 1, 2021
  17. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Actually, ImprovedPerlinNoise2D shader has dedicated frequency controls. Why not just use them instead? As for an offset, I'm out of ideas, it should work. Make your o.uv explicitly float2, may it being float4 affects something, idk..

    My shader code with noise I took somewhere looks like this:
    Code (CSharp):
    1. Shader "CustomRP/TestBlit"
    2. {
    3.     Properties
    4.     {
    5.         _Scale ("Scale", Vector) = (1, 1, 0, 0)
    6.         _Offset("Offset", Vector) = (0, 0, 0, 0)
    7.         _Zoom ("Zoom", Range(1, 400)) = 1
    8.     }
    9.  
    10.     SubShader
    11.     {
    12.         Cull Off ZWrite Off
    13.  
    14.         Pass
    15.         {
    16.             HLSLPROGRAM
    17.             #pragma target 3.5
    18.             #pragma vertex Vert
    19.             #pragma fragment Frag
    20.  
    21.             #include "Assets/Custom RP/Shader Library/Common.hlsl"
    22.  
    23.             struct Attributes
    24.             {
    25.                 float3 positionOS : POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.             };
    28.  
    29.             struct Varyings
    30.             {
    31.                 float4 positionCS : SV_POSITION;
    32.                 float2 uv : VAR_UV;
    33.             };
    34.  
    35.             float2 _Scale;
    36.             float2 _Offset;
    37.             float _Zoom;
    38.  
    39.             Varyings Vert(Attributes input)
    40.             {
    41.                 Varyings output;
    42.                 output.positionCS = TransformObjectToHClip(input.positionOS);
    43.                 output.uv = _Zoom * (input.uv - 0.5) * _Scale + _Offset;
    44.                 return output;
    45.             }
    46.  
    47.  
    48.             float4 permute(float4 x)
    49.             {
    50.                 return fmod(34.0 * pow(x, 2) + x, 289.0);
    51.             }
    52.  
    53.             float2 fade(float2 t) {
    54.                 return 6.0 * pow(t, 5.0) - 15.0 * pow(t, 4.0) + 10.0 * pow(t, 3.0);
    55.             }
    56.  
    57.             float4 taylorInvSqrt(float4 r) {
    58.                 return 1.79284291400159 - 0.85373472095314 * r;
    59.             }
    60.  
    61.             #define DIV_289 0.00346020761245674740484429065744f
    62.  
    63.             float mod289(float x) {
    64.                 return x - floor(x * DIV_289) * 289.0;
    65.             }
    66.  
    67.             float PerlinNoise2D(float2 P)
    68.             {
    69.                 float4 Pi = floor(P.xyxy) + float4(0.0, 0.0, 1.0, 1.0);
    70.                 float4 Pf = frac(P.xyxy) - float4(0.0, 0.0, 1.0, 1.0);
    71.  
    72.                 float4 ix = Pi.xzxz;
    73.                 float4 iy = Pi.yyww;
    74.                 float4 fx = Pf.xzxz;
    75.                 float4 fy = Pf.yyww;
    76.  
    77.                 float4 i = permute(permute(ix) + iy);
    78.  
    79.                 float4 gx = frac(i / 41.0) * 2.0 - 1.0;
    80.                 float4 gy = abs(gx) - 0.5;
    81.                 float4 tx = floor(gx + 0.5);
    82.                 gx = gx - tx;
    83.  
    84.                 float2 g00 = float2(gx.x, gy.x);
    85.                 float2 g10 = float2(gx.y, gy.y);
    86.                 float2 g01 = float2(gx.z, gy.z);
    87.                 float2 g11 = float2(gx.w, gy.w);
    88.  
    89.                 float4 norm = taylorInvSqrt(float4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
    90.                 g00 *= norm.x;
    91.                 g01 *= norm.y;
    92.                 g10 *= norm.z;
    93.                 g11 *= norm.w;
    94.  
    95.                 float n00 = dot(g00, float2(fx.x, fy.x));
    96.                 float n10 = dot(g10, float2(fx.y, fy.y));
    97.                 float n01 = dot(g01, float2(fx.z, fy.z));
    98.                 float n11 = dot(g11, float2(fx.w, fy.w));
    99.  
    100.                 float2 fade_xy = fade(Pf.xy);
    101.                 float2 n_x = lerp(float2(n00, n01), float2(n10, n11), fade_xy.x);
    102.                 float n_xy = lerp(n_x.x, n_x.y, fade_xy.y);
    103.                 return 2.3 * n_xy;
    104.             }
    105.  
    106.             float4 Frag(Varyings input) : SV_Target
    107.             {
    108.                 return PerlinNoise2D(input.uv);
    109.             }      
    110.  
    111.             ENDHLSL
    112.         }
    113.     }
    114. }
    To draw it on the screen I do just this
    Code (CSharp):
    1. buffer.Blit(null, BuiltinRenderTextureType.CameraTarget, noiseMaterial);
    I use custom render pipeline, so the rest of the code is unlikely to be of any value.
     
    Last edited: Jan 1, 2021
  18. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    I am using the frequency controls from ImprovedPerlinNoise2D, I'm not sure what you mean.

    See here:

    Code (CSharp):
    1. namespace ImprovedPerlinNoiseProject
    2. {
    3.     public class ExampleGPU_2D : MonoBehaviour
    4.     {
    5.  
    6.         public NOISE_STLYE m_stlye = NOISE_STLYE.FBM;
    7.  
    8.         public int m_seed = 0;
    9.  
    10.         public float m_frequency = 1.0f;
    11.  
    12.         public float m_lacunarity = 2.0f;
    13.  
    14.         public float m_gain = 0.5f;
    15.         public Vector2 m_scale = new Vector2(1, 1);
    16.         public Vector2 m_offset = new Vector2(0, 0);
    17.  
    18.         private Renderer m_renderer;
    19.  
    20.         private GPUPerlinNoise m_perlin;
    21.  
    22.         public int width = 1408;
    23.         public int height = 512;
    24.  
    25.         void Start()
    26.         {
    27.             m_perlin = new GPUPerlinNoise(m_seed);
    28.  
    29.             m_perlin.LoadResourcesFor2DNoise();
    30.  
    31.             m_renderer = GetComponent<Renderer>();
    32.  
    33.             m_renderer.material.SetTexture("_PermTable1D", m_perlin.PermutationTable1D);
    34.             m_renderer.material.SetTexture("_Gradient2D", m_perlin.Gradient2D);
    35.  
    36.             if ((float)Screen.width / Screen.height < (float)width / height)
    37.             {
    38.                 Camera.main.orthographicSize = (float)width / 2 * (float)Screen.height / Screen.width;
    39.             }
    40.             else
    41.             {
    42.                 Camera.main.orthographicSize = (float)height / 2;
    43.             }
    44.             Camera.main.orthographicSize = Camera.main.orthographicSize * 10;
    45.         }
    46.  
    47.         public void SaveTextureFromShader()
    48.         {
    49.             RenderTexture renderTexture = RenderTexture.GetTemporary(width, height);
    50.             Graphics.Blit(null, renderTexture, m_renderer.material);
    51.             Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
    52.             texture.filterMode = FilterMode.Bilinear;
    53.             texture.wrapMode = TextureWrapMode.Clamp;
    54.             RenderTexture.active = renderTexture;
    55.             texture.ReadPixels(new Rect(Vector2.zero, new Vector2(width, height)), 0, 0);
    56.             RenderTexture.active = null;
    57.             RenderTexture.ReleaseTemporary(renderTexture);
    58.  
    59.             byte[] bytes = texture.EncodeToPNG();
    60.             File.WriteAllBytes(Application.dataPath + "/Temp/FromShader.png", bytes);
    61.         }
    62.  
    63.         void Update()
    64.         {
    65.             float aspect = (float)width / height;
    66.             m_scale.x = 1;
    67.             m_scale.y = 1;
    68.  
    69.             if (aspect > 1)
    70.             {
    71.                 m_scale.x = aspect;
    72.             }
    73.             else
    74.             {
    75.                 m_scale.y = (float)height / width;
    76.             }
    77.  
    78.             m_renderer.material.SetFloat("_Frequency", m_frequency);
    79.             m_renderer.material.SetFloat("_Lacunarity", m_lacunarity);
    80.             m_renderer.material.SetFloat("_Gain", m_gain);
    81.             m_renderer.material.SetFloat("_NoiseStyle", (float)m_stlye);
    82.             m_renderer.material.SetVector("_Scale", m_scale);
    83.             m_renderer.material.SetVector("_Offset", m_offset);
    84.  
    85.             m_renderer.transform.localScale = new Vector3(width, 1, height);
    86.             if ((float)Screen.width / Screen.height < (float)width / height)
    87.             {
    88.                 Camera.main.orthographicSize = (float)width / 2 * (float)Screen.height / Screen.width;
    89.             }
    90.             else
    91.             {
    92.                 Camera.main.orthographicSize = (float)height / 2;
    93.             }
    94.  
    95.             Camera.main.orthographicSize = Camera.main.orthographicSize * 10;
    96.  
    97.             // Save();
    98.             if (Input.GetKeyDown(KeyCode.Space))
    99.             {
    100.                 SaveTextureFromShader();
    101.             }
    102.         }
    103.  
    104.     }
    105.  
    106. }
    Frequency is controlled via _Frequency and is used in the fragment shader. Again remember my _Scale is NOT zoom, its just the "tiling" so it accounts for the aspect ratio of the plane.

    I think you're zoom is simply enlarging/shrinking the "texture" and isn't affecting sampling (which is frequency for the octaves); so your zoom is always centered but if you actually change the frequency and the offsets without touching zoom you'll see you'll have the same problem I have.

    e: Like looking closer you don't actually have frequency. If you use the example from the github I linked I think we'll prove that we can't actually affect the "Zoom" via frequency in the vert shader.

    e2: Your shader works albeit with Zoom and Frequency being different indeed if I change it (output uvs) from float4 to float2 which is strange. What happens is that if I change the offsets and THEN change frequency its "zoom" effect is wrong (centered away from offset), but the Zoom attribute always works to keep it centered. So if I change frequency first, then move with offsets, then zoom in that order in the inspector I get the results I want; but if I change frequency after that it'll shift towards the offset point instead of the center, while zoom always works at the center.
     
    Last edited: Jan 1, 2021
  19. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Previously I thought that you are using _Scale instead of built-in frequency parameters. Otherwise, why do you need this _Scale at all? As for tiling-vs-frequency problem, I honestly don't get the difference. If I multiply uv by 10, it literally means increasing observed frequency 10 times, be it a sine wave or perlin noise.
    111.PNG
    Now, when using built-in frequency parameters - yes, they don't take into account custom offset. Noise is centered at (0, 0) and "scales" around this point. Probably there are methods I'm not aware of, to make it scale around observer. And I don't really want to dig into this shader and figure it out.
     
  20. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    I have a plane. The plane is not always going to be square. It might be 512 by 512 or it might be 1408 by 512. If I scale the plane to be 1408,1,512 then the result without any other change is going to be distorted/stretched. _Scale is not *scale*-scale, its more like the tiling.

    Trust me that there's a reason for the separation between _Scale and Frequency for my specific use case. I am producing a texture to form the height map for procedural terrain generation, so it is needed that the texture is not always a power of 2 or square. _Scale fixes the distortion, for example if width is 1408 and height is 512, then _Scale is automatically set to be 2.75, 1. I don't touch scale once its passed in. I only want to touch frequency for the zoom and offset to scroll around the result.

    Right now Frequency and "Zoom" differ in that Zoom seems to let me always stay centered, while Frequency is distorted and will zoom away/towards the offset and not the center. It absolutely makes a pretty big difference for my specific use case.

    So what I would like, and this works in C# for a CPU version of FBM noise like so:


    Code (CSharp):
    1. float sampleX = ((x - halfWidth) / scale * frequency) + (octaveOffsets[i].x / scale * frequency);
    2. float sampleY = ((y - halfHeight) / scale * frequency) + (octaveOffsets[i].y / scale * frequency);
    From Sebastian Lague's procedural landmass generation tutorial. This allows for frequency to adjust the zoom level regardless of offset but remains centered. Its possible for sure but in the shader it seems to be difficult to get this same effect.

    Basically ideally I should be able to recreate this from some combination of the vert and frag shaders, hence why I had offsets in frag earlier because that *does* work *almost* perfectly, the problem is the parallax that it generates. If you recreate my project from what I posted you'll see my problem and why its a problem.
     
  21. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Okay, you talked me into it.. Of course, we need to divide by frequency, as your C# version rightly suggests.



    Code (CSharp):
    1.             v2f vert (appdata_base v)
    2.             {
    3.                 v2f o;
    4.                 o.pos = UnityObjectToClipPos(v.vertex);
    5.                 o.uv = _Scale * (v.texcoord - 0.5) + _Offset / _Frequency;
    6.                 return o;
    7.             }
     
    Last edited: Jan 2, 2021
  22. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Video is gone D:
     
  23. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Now its back, accidentally deleted with some old vids.
     
  24. TheProfessor

    TheProfessor

    Joined:
    Nov 2, 2014
    Posts:
    74
    Thanks! So that works, but now we have the other problem that it now retains parallax. I.e the noise appears to be on multiple different "layers" moving at different speeds, like it looks like there's a background moving and the foreground is like clouds, do you see?

    e: Actually it's unclear to me if you have the issue, but you also moved things a little fast, if you more slowly manipulate the offset with the frequency at some value like 5, do you notice if there's parallax?

    e2: AHA! This time the issue was mine, I had some code I forgot to revert when I tried your suggestion, it now works perfectly I think!

    e3: Yup, it works now, thanks for sticking through this with me!
     
    Last edited: Jan 2, 2021
  25. AlexTorbin

    AlexTorbin

    Joined:
    Dec 12, 2019
    Posts:
    48
    Finally :D
     
    TheProfessor likes this.