Search Unity

Camera raycast error

Discussion in 'Scripting' started by acropole, Dec 23, 2020.

  1. acropole

    acropole

    Joined:
    Aug 13, 2009
    Posts:
    171
    hi,

    The following code is supposed to cast a ray from camera to almost each pixel in camera's view, but the result is wrong. Why ?

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. [CustomEditor(typeof(Camera))]
    7. public class CameraEditor:Editor
    8. {
    9.     private void OnSceneGUI()
    10.     {
    11.         if (Event.current.type != EventType.Repaint)
    12.             return;
    13.  
    14.         Camera camera = target as Camera;
    15.         Transform t = camera.transform;
    16.         float h = Mathf.Tan(camera.fieldOfView);
    17.         float w = h * camera.aspect;
    18.  
    19.         Vector3
    20.             forward = t.forward,
    21.             right = t.right,
    22.             up = t.up,
    23.             dir, cameraPos = t.position;
    24.         Debug.Log(h + ":" + w);
    25.  
    26.         int count = (camera.scaledPixelWidth * camera.pixelHeight);
    27.  
    28.         Handles.Label(cameraPos, "Pixel Count : " + count + " w:" + camera.pixelWidth + " h:" + camera.pixelHeight);
    29.         float x, y;
    30.         for(int i = 0; i < count; i+=1000)
    31.         {
    32.             x = i % camera.pixelWidth - camera.pixelWidth * 0.5f;
    33.             y = i / camera.pixelHeight - camera.pixelHeight * 0.5f;
    34.  
    35.             dir = (forward + x * w * right + y * h * up).normalized;
    36.  
    37.             Handles.color = new Color(dir.x, dir.y, dir.z, 1);
    38.             //Handles.Label(cameraPos + dir, x + ":" + y + ":" + dir);
    39.             Handles.DrawLine(cameraPos, cameraPos + dir);
    40.         }
    41.     }
    42. }
    43.  
    Result :

    camera-raycast-pixels.png

    Thanks
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,740
    Line 33 your modulo should be with the width, not with height.

    When one iterates all the pixels on a raster screen from 0 to w*h-1, one derives cartesian coordinates with:

    Code (csharp):
    1. int x = i % w;
    2. int y = i / w;
    You don't use h for that purpose, assuming you're going across scanline wise, then down one line, etc.

    PS: note that your for loop is also stepping by +1000 right now, but I assume that is for rapid testing purposes...
     
    Last edited: Dec 23, 2020
  3. acropole

    acropole

    Joined:
    Aug 13, 2009
    Posts:
    171
    Yes, i++ draw millions handles lines and kill the fps.
    I found another solution but yours is shorter. Thanks.

    I tried to port this to my post process shader but it still buggy. It is supposed to draw pixel ray or white if pointing at a sphere at world center with a radius of 2. But it only draw the ray as a color or full white if the camera is inside the sphere. It's a raymarching with signed distance function.

    Code (CSharp):
    1. Shader "Hidden/Custom/RayMarcher"
    2. {
    3.     HLSLINCLUDE
    4. #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"
    5. #include "DistanceFunctions.cginc"
    6.  
    7.     uniform float3
    8.         forward,
    9.         right,
    10.         up;
    11.  
    12.     uniform float _maxDistance;
    13.     uniform float fov;
    14.     uniform float aspect;
    15.  
    16.     struct VertexInput
    17.     {
    18.         float4 vertex : POSITION;
    19.     };
    20.  
    21.     struct v2f
    22.     {
    23.         float4 vertex : SV_POSITION;
    24.         float4 ray : TEXCOORD0;
    25.     };
    26.  
    27.     inline float4 ComputeNonStereoScreenPos(float4 pos) {
    28.         float4 o = pos * 0.5f;
    29.         o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;
    30.         o.zw = pos.zw;
    31.         return o;
    32.     }
    33.  
    34.     inline float4 ComputeScreenPos(float4 pos) {
    35.         float4 o = ComputeNonStereoScreenPos(pos);
    36. #if defined(UNITY_SINGLE_PASS_STEREO)
    37.         o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
    38. #endif
    39.         return o;
    40.     }
    41.  
    42.     v2f vert(VertexInput v)
    43.     {
    44.         v2f o;
    45.  
    46.         o.vertex = float4(v.vertex.xyz, 1.0);
    47.  
    48.         float4 screenpos = ComputeScreenPos(v.vertex);
    49.         o.ray = screenpos;
    50.  
    51.         return o;
    52.     }
    53.  
    54.     float distanceField(float3 p) {
    55.         return sdSphere(p - float3(0, 0, 0), 2.0);
    56.     }
    57.  
    58.     float4 raymarching(float3 ro, float3 rd, float depth)
    59.     {
    60.         float4 result = float4(0, 0, 0, 1);
    61.  
    62.         const int maxi = 64;
    63.         float t = 0;
    64.         float d;
    65.         float3 p;
    66.         for (int i = 0; i < maxi; i++)
    67.         {
    68.             if (t > _maxDistance)
    69.             {
    70.                 result = float4(rd, 1);
    71.                 break;
    72.             }
    73.  
    74.             p = ro + rd * t;
    75.             d = distanceField(p);
    76.             if (d < 0.01) {
    77.                 result = float4(1, 1, 1, 1);
    78.                 break;
    79.             }
    80.  
    81.             t += d;
    82.         }
    83.  
    84.         return result;
    85.     }
    86.  
    87.     static float h = tan(fov);
    88.     static float w = h * aspect;
    89.  
    90.     float4 frag(v2f i) : SV_Target
    91.     {
    92.         float3 rayOrigin = _WorldSpaceCameraPos;
    93.  
    94.         float x = i.ray.x * 2.0f - 1.0f;
    95.         float y = i.ray.y * 2.0f - 1.0f;
    96.  
    97.         float3 dir = normalize(forward + x * w * right + y * h * up);
    98.  
    99.         float4 result = raymarching(rayOrigin, dir, 1000);
    100.         return result;
    101.     }
    102.     ENDHLSL
    103.  
    104.     SubShader
    105.     {
    106.         // No culling or depth
    107.         Cull Off
    108.         ZWrite Off
    109.         ZTest Always
    110.  
    111.         Pass
    112.         {
    113.             HLSLPROGRAM
    114.             #pragma vertex vert
    115.             #pragma fragment frag
    116.             #pragma target 3.0
    117.             ENDHLSL
    118.         }
    119.     }
    120. }
    The raymarcher c# class :

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. using UnityEngine.Rendering.PostProcessing;
    7.  
    8. [Serializable][ExecuteAlways]
    9. [PostProcess(typeof(RayMarcherRenderer), PostProcessEvent.AfterStack, "Custom/RayMarcher", true)]
    10. public class RayMarcher : PostProcessEffectSettings
    11. {
    12.     public float _maxDistance;
    13.  
    14.     public FloatParameter MaxDistance = new FloatParameter { value = 100.0f };
    15. }
    16.  
    17. [ExecuteAlways]
    18. public sealed class RayMarcherRenderer : PostProcessEffectRenderer<RayMarcher>
    19. {
    20.     public override void Render(PostProcessRenderContext context)
    21.     {
    22.         PropertySheet sheet = context.propertySheets.Get("Hidden/Custom/RayMarcher");
    23.  
    24.         Camera camera = context.camera;
    25.  
    26.         sheet.properties.SetVector("forward", camera.transform.forward);
    27.         sheet.properties.SetVector("right", camera.transform.right);
    28.         sheet.properties.SetVector("up", camera.transform.up);
    29.         sheet.properties.SetFloat("fov", camera.fieldOfView * Mathf.Deg2Rad * 0.5f);
    30.         sheet.properties.SetFloat("aspect", camera.aspect);
    31.         sheet.properties.SetFloat("_MaxDistance", settings.MaxDistance);
    32.         context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0);
    33.     }
    34. }
    35.  
    And the result looks ok for the rays, but the sphere is nowhere :

    raymarcher.png
     
    Last edited: Dec 26, 2020
  4. acropole

    acropole

    Joined:
    Aug 13, 2009
    Posts:
    171
    I screwed up with _maxDistance _MaxDistance and MaxDistance...