Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
213 views
in Technique[技术] by (71.8m points)

unity3d - Rendering Only point cloud data inside a box

enter image description here

I am trying to render only point cloud data inside a 3d box with a shader. However, a point cloud data shader uses geometry and a clip box shader uses surface, so I do not know how to combine these two together.

Point Cloud Data Shader

https://answers.unity.com/questions/1437520/implementing-a-geometry-shader-for-a-pointcloud.html

///////////////////////////////////////////

 

   Shader "Custom/Pointcloud" {
    Properties{
        _Radius("Sphere Radius", float) = 1.0
        
    }
        SubShader{
       LOD 200
       Tags { "RenderType" = "Opaque" }

        //if you want transparency
        //Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
        //Blend SrcAlpha OneMinusSrcAlpha
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma geometry geom
            #pragma target 4.0                  // Use shader model 3.0 target, to get nicer looking lighting
            #include "UnityCG.cginc"
            struct vertexIn {
                float4 pos : POSITION;
                float4 color : COLOR;
            };
            struct vertexOut {
                float4 pos : SV_POSITION;
                float4 color : COLOR0;
                float3 normal : NORMAL;
                float r : TEXCOORD0; // not sure if this is good to do lol
            };
            struct geomOut {
                float4 pos : POSITION;
                float4 color : COLO0R;
                float3 normal : NORMAL;
            };

            float rand(float3 p) {
                return frac(sin(dot(p.xyz, float3(12.9898, 78.233, 45.5432))) * 43758.5453);
            }
            float2x2 rotate2d(float a) {
               float s = sin(a);
               float c = cos(a);
               return float2x2(c,-s,s,c);
            }
            //Vertex shader: computes normal wrt camera
            vertexOut vert(vertexIn i) {
                vertexOut o;
                o.pos = UnityObjectToClipPos(i.pos);
                o.color = i.color;
                o.normal = ObjSpaceViewDir(o.pos);
                
                o.r = rand(i.pos);// calc random value based on object space pos
                // from world space instead (particles will spin when mesh moves, kinda funny lol)
                //o.r = rand(mul(unity_ObjectToWorld,i.pos));
                return o;
            }

            float _Radius;
            //Geometry shaders: Creates an equilateral triangle with the original vertex in the orthocenter
            [maxvertexcount(3)]
            void geom(point vertexOut IN[1], inout TriangleStream<geomOut> OutputStream)
            {
               float2 dim = float2(_Radius,_Radius);

               float2 p[3];    // equilateral tri
               p[0] = float2(-dim.x, dim.y * .57735026919);
               p[1] = float2(0., -dim.y * 1.15470053838);
               p[2] = float2(dim.x, dim.y * .57735026919);

               float2x2 r = rotate2d(IN[0].r * 3.14159);

               geomOut OUT;
              // OUT.color = IN[0].color;
             
               OUT.color = IN[0].color;
               OUT.normal = IN[0].normal;

               for (int i = 0; i < 3; i++) {
                   p[i] = mul(r,p[i]);    // apply rotation
                   p[i].x *= _ScreenParams.y / _ScreenParams.x; // make square
                   OUT.pos = IN[0].pos + float4(p[i],0,0) / 2.;
                   OutputStream.Append(OUT);
               }
            }
            float4 frag(geomOut i) : COLOR
            {
                return i.color;
            // could do some additional lighting calculation here based on normal
        }
        ENDCG
    }
    }
        FallBack "Diffuse"
} 


    
    

ClibBox shader

https://answers.unity.com/questions/1762908/render-only-whats-inside-a-box.html

   Shader "Custom/ClipBox" {
        Properties{
            _MainTex("Albedo (RGB)", 2D) = "white" {}
            _Glossiness("Smoothness", Range(0,1)) = 0.5
            _Metallic("Metallic", Range(0,1)) = 0.0
        }
    
            SubShader{
                Tags { "RenderType" = "Opaque" }
                LOD 200
    
                CGPROGRAM
                #pragma surface surf Standard fullforwardshadows addshadow
                #pragma target 3.0
    
                sampler2D _MainTex;
                half _Glossiness;
                half _Metallic;
                float4x4 _WorldToBox;
    
                struct Input {
                    float2 uv_MainTex;
                    float3 worldPos;
                };
    
                void surf(Input IN, inout SurfaceOutputStandard o) {
                    float3 boxPosition = mul(_WorldToBox, float4(IN.worldPos, 1));
                    clip(boxPosition + 0.5);
                    clip(0.5 - boxPosition);
    
                    fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
                    o.Albedo = c.rgb;
                    o.Metallic = _Metallic;
                    o.Smoothness = _Glossiness;
                    o.Alpha = c.a;
                    o.Alpha = 0.0f;
                }
                ENDCG
            }
                FallBack "Diffuse"
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To get the world position of your pixel in the fragment shader you have to pass it through your vertex and geometry shader:

vertex => geometry

struct vertexOut {
    float4 pos : SV_POSITION;
    float4 color : COLOR0;
    float3 normal : NORMAL;
    float r : TEXCOORD0; // not sure if this is good to do lol
    float3 worldPos : TEXCOORD1;
};

vertexOut vert(vertexIn i) {
    vertexOut o;
    ...
    // calculate world position
    o.worldPos = mul(unity_ObjectToWorld, i.pos);
    return o;
}

geometry => fragment (Since you simply create a small triangle around the vertex you can approximate the new vertices' world positions with the one from the original vertex. If this is undesirable you have to calculate 3 separate world positions inside your loop.)

struct geomOut {
    float4 pos : POSITION;
    float4 color : COLO0R;
    float3 normal : NORMAL;
    float3 worldPos : TEXCOORD0;
};

void geom(point vertexOut IN[1], inout TriangleStream<geomOut> OutputStream) {
    ...
    for (int i = 0; i < 3; i++) {
        p[i] = mul(r,p[i]);    // apply rotation
        p[i].x *= _ScreenParams.y / _ScreenParams.x; // make square
        OUT.pos = IN[0].pos + float4(p[i],0,0) / 2.;
        // Simply use the input vertex world position. This might result in unclear cube edges.
        OUT.worldPos = IN[0].worldPos;
        OutputStream.Append(OUT);
    }
}

Now you can add the clipping code

float3 boxPosition = mul(_WorldToBox, float4(IN.worldPos, 1));
clip(boxPosition + 0.5);
clip(0.5 - boxPosition);

and the _WorldToBox property to your fragment shader. You also need the c# scipt passing the matrix to the shader.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...