Search Unity

  1. Get the latest news, tutorials and offers directly to your inbox with our newsletters. Sign up now.
    Dismiss Notice

I created a circuit shader, but I can't seem to figure out how to stop it from constantly flashing.

Discussion in 'Shaders' started by asperatology, Apr 25, 2019.

  1. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    976
    This is the full code of the Circuitry compute shader that I wrote:

    Code (CSharp):
    1. // Implementation of the Circuitry.
    2. // 1. Any live electric spark with fewer than 2 live neighbors will be grounded. Represents "underpopulation".
    3. // 2. Any live electric spark with 2 or 3 live neighbors will conduct to the next cell. Represents "normal population".
    4. // 3. Any live electric spark with more than 3 live neighbors will be grounded. Represents "overpopulation".
    5. // 4. Any grounded electric spark with exactly 3 live neighbors become a live electric spark. Represents "reproduction".
    6.  
    7.  
    8. // Each #kernel tells which function to compile; you can have many kernels
    9. #pragma kernel Circuitry
    10. #pragma enable_d3d11_debug_symbols
    11.  
    12. //Because this is a compute shader.
    13.  
    14. // Size of the mesh's canvas.
    15. float Width;
    16. float Height;
    17.  
    18. // The input's texture
    19. sampler2D Input;
    20.  
    21. // Creates a RenderTexture for us to output to, as the result.
    22. RWTexture2D<float4> Result;
    23.  
    24. float ClampValue(float value, float clamp) {
    25.     if (value < 0.0f)
    26.         return (1.0f + value) % clamp;
    27.     if (value > 1.0f)
    28.         return (value - 1.0f) % clamp;
    29.     return value % clamp;
    30. }
    31.  
    32. [numthreads(8, 8, 1)]
    33. void Circuitry(uint3 id : SV_DispatchThreadID) {
    34.     //RenderTexture coordinates on Direct3D-like platforms (Windows) will be flipped upside down.
    35.     //Need to re-flip the Y coordinates back around to fix this issue.
    36.     //https://docs.unity3d.com/Manual/SL-PlatformDifferences.html
    37.  
    38.     //Calculate the current pixel's position.
    39.     float3 position = float3((id.x / Width), (id.y / Height), 0.0f);
    40. #if UNITY_UV_STARTS_AT_TOP
    41.     position.y = (Height - id.y - 1) / Height;
    42. #endif
    43.  
    44.     //Calculate the current pixel's size.
    45.     float2 pixelSize = float2(1.0 / Width, 1.0 / Height);
    46.  
    47.     //Get the current pixel based on the input texture's level of detail color value.
    48.     float4 currentPixel = tex2Dlod(Input, float4(position.x, position.y, position.z, 0));
    49.  
    50.     //Main loop - Blur the rendered input texture until it can't be blurred anymore.
    51.  
    52.     // Start from +Y, and go towards -Y. For each row, calculate the columns. Blurring grabs the average pixel in a 3x3 area, then apply the average to the result texture for that 3x3 area.
    53.     // Create a buffer pixel that will hold the average pixel values of a given 3x3 area.
    54.     float4 neighborPixels = float4(0, 0, 0, 0);
    55.  
    56.     // +Y row
    57.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x + pixelSize.x, Width), ClampValue(position.y + pixelSize.y, Height), position.z, 0));
    58.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x, Width), ClampValue(position.y + pixelSize.y, Height), position.z, 0));
    59.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x - pixelSize.x, Width), ClampValue(position.y + pixelSize.y, Height), position.z, 0));
    60.  
    61.     // Center row
    62.     // We do not include the current pixel, just yet.
    63.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x + pixelSize.x, Width), ClampValue(position.y, Height), position.z, 0));
    64.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x - pixelSize.x, Width), ClampValue(position.y, Height), position.z, 0));
    65.  
    66.     // -Y row
    67.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x + pixelSize.x, Width), ClampValue(position.y - pixelSize.y, Height), position.z, 0));
    68.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x, Width), ClampValue(position.y - pixelSize.y, Height), position.z, 0));
    69.     neighborPixels += tex2Dlod(Input, float4(ClampValue(position.x - pixelSize.x, Width), ClampValue(position.y - pixelSize.y, Height), position.z, 0));
    70.  
    71.  
    72.     //Logic
    73.     float3 pixelResult = float3(currentPixel.rgb);
    74.     // Check only the red component of the pixel color, and check the accumulated value.
    75.     if (currentPixel.r >= 0.5) {
    76.         // Pixel is alive.
    77.         // First, check Rule 2.
    78.         if (neighborPixels.r > 1.5 && neighborPixels.r < 3.5) {
    79.             // Margin of error is +/-0.5  (2 - 0.5, 3 + 0.5)
    80.             // Pixel is alive, thus we set the final pixel to white
    81.             pixelResult = float3(1, 1, 1);
    82.         }
    83.         else if (currentPixel.g > 0.004f) {
    84.             // Pixel is dead, but it still has life. We need to fade it away.
    85.             pixelResult = float3(max(currentPixel.b - 0.05, 0.49f), max(currentPixel.g - 0.005, 0.49f), 0.0);
    86.         }
    87.         else {
    88.             // Pixel is dead, thus we set the final pixel to black.
    89.             pixelResult = float3(0.0, 0.0, 0.0);
    90.         }
    91.     }
    92.     else {
    93.         // Pixel is dead.
    94.         if (neighborPixels.r > 2.5 && neighborPixels.r < 3.5) {
    95.             // Roughly equal to 3, with margin of error +/-0.5, (3 - 0.5, 3 + 0.5)
    96.             // Therefore, pixel is alive thanks to Rule 3.
    97.             pixelResult = float3(1, 1, 1);
    98.         }
    99.         else if (currentPixel.g > 0.004f) {
    100.             // Pixel is dead, but it still has life. We need to fade it away.
    101.             pixelResult = float3(min(currentPixel.b + 0.04, 0.49f), max(currentPixel.g - 0.004, 0.49f), 0.0);
    102.         }
    103.         else {
    104.             // Pixel is dead, thus we set the final pixel to black.
    105.             pixelResult = float3(0.0, 0.0, 0.0);
    106.         }
    107.     }
    108.     Result[id.xy] = float4(pixelResult, 1.0f);
    109. }
    110.  
    111.  
    And here's how it looks:




    I'm having trouble making it stop flashing too much. But I have no idea how to make it stop.

    The goal here is to simulate the movement of electric sparks going along the conduits.

    Can someone help?

    Script to attach this compute shader to:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Circuitry : MonoBehaviour {
    6.     public ComputeShader computeShader;
    7.     public List<RenderTexture> outputs;
    8.     public Material material;
    9.     public Texture input;
    10.  
    11.     public int width = 512;
    12.     public int height = 512;
    13.     public int millisecondsPerTick = 50;
    14.     public ulong generation = 0;
    15.  
    16.     protected int outputIndex;
    17.     protected float delaySeconds = 0f;
    18.     protected float elapsedDeltaTime = 0f;
    19.  
    20.     void Start() {
    21.         MeshRenderer renderer = this.GetComponent<MeshRenderer>();
    22.         this.material = renderer.material;
    23.  
    24.         for (int i = 0; i < 2; i++) {
    25.             RenderTexture output = new RenderTexture(this.width, this.height, 32) {
    26.                 wrapMode = TextureWrapMode.Repeat,
    27.                 enableRandomWrite = true,
    28.                 filterMode = FilterMode.Point,
    29.                 useMipMap = false
    30.             };
    31.             output.Create();
    32.             this.outputs.Add(output);
    33.         }
    34.     }
    35.  
    36.     void Update() {
    37.         if (this.height < 1 || this.width < 1 || !this.input)
    38.             return;
    39.  
    40.         this.delaySeconds = (this.millisecondsPerTick / 1000f);
    41.  
    42.         this.elapsedDeltaTime += Time.deltaTime;
    43.         if (this.elapsedDeltaTime < this.delaySeconds) {
    44.             return;
    45.         }
    46.         this.elapsedDeltaTime = 0f;
    47.  
    48.         int kernel = this.computeShader.FindKernel("Circuitry");
    49.         this.computeShader.SetTexture(kernel, "Input", this.input);
    50.         this.computeShader.SetFloat("Width", this.width);
    51.         this.computeShader.SetFloat("Height", this.height);
    52.  
    53.         this.computeShader.SetTexture(kernel, "Result", this.outputs[this.outputIndex]);
    54.         this.computeShader.Dispatch(kernel, this.width / 8, this.height / 8, 1);
    55.  
    56.         this.input = this.outputs[this.outputIndex];
    57.         this.material.mainTexture = this.outputs[this.outputIndex];
    58.         this.outputIndex = ++this.outputIndex % this.outputs.Count;
    59.  
    60.         this.generation++;
    61.      }
    62. }
    63.  
    1. Add a script component to an object.
    2. Provide an "input" PNG file.
    3. Set the shader to it.
    4. Run.
     
    Last edited: Apr 25, 2019
unityunity