You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity正交相机下实时显示网格与3D圆锥体交截面的实现

可行方案:基于片元着色器的实时圆锥相交遮罩

我刚好有个完美适配你需求的方案——用片元着色器直接做实时相交判断,完全避开你之前踩过的CSG、网格重构这类性能大坑,而且天生支持正交相机,每帧都能实时更新。

核心思路

因为你提到圆锥相对于目标物体尺寸极大,我们可以把判断逻辑简化到极致:

  • 把物体表面每个片元的世界坐标,和圆锥的顶点、轴线做数学判断
  • 只保留那些落在圆锥内部的片元,直接丢弃(或透明化)外部的像素
  • 所有计算都在GPU并行执行,性能开销极低,完全满足实时要求

具体实现

1. 适配URP的Shader代码(正交相机可用)

这个Shader会在片元阶段判断当前像素是否在圆锥范围内,不在就直接丢弃,剩下的就是相交区域:

Shader "Custom/ConeIntersectionMask"
{
    Properties
    {
        _BaseColor ("Base Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        // 圆锥参数
        _ConeTipWorldPos ("Cone Tip World Position", Vector) = (0,0,0,1)
        _ConeAxisWorldDir ("Cone Axis World Direction", Vector) = (0,0,1,0)
        _ConeHalfAngleDeg ("Cone Half Angle (Degrees)", Float) = 45.0
        _ConeMaxLength ("Cone Max Length", Float) = 1000.0 // 设得足够大覆盖场景
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
        LOD 200

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
                float3 normalOS     : NORMAL;
            };

            struct Varyings
            {
                float2 uv           : TEXCOORD0;
                float3 positionWS   : TEXCOORD1;
                float4 positionHCS  : SV_POSITION;
                float3 normalWS     : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _BaseColor;
            float _Glossiness;
            float _Metallic;

            float3 _ConeTipWorldPos;
            float3 _ConeAxisWorldDir;
            float _ConeHalfAngleDeg;
            float _ConeMaxLength;

            Varyings vert (Attributes input)
            {
                Varyings output;
                output.positionHCS = TransformObjectToHClip(input.positionOS.xyz);
                output.positionWS = TransformObjectToWorld(input.positionOS.xyz);
                output.normalWS = TransformObjectToWorldNormal(input.normalOS);
                output.uv = TRANSFORM_TEX(input.uv, _MainTex);
                return output;
            }

            half4 frag (Varyings input) : SV_Target
            {
                // 计算片元到圆锥顶点的向量
                float3 pointToTip = input.positionWS - _ConeTipWorldPos;
                // 归一化圆锥轴线(避免方向向量长度不一致的问题)
                float3 coneAxis = normalize(_ConeAxisWorldDir);

                // 计算片元在圆锥轴线上的投影长度(轴向距离)
                float axialDistance = dot(pointToTip, coneAxis);
                // 轴向距离必须在0到最大长度之间(如果要做双向圆锥,去掉axialDistance < 0的判断即可)
                if (axialDistance < 0 || axialDistance > _ConeMaxLength)
                {
                    discard; // 不在圆锥长度范围内,直接丢弃像素
                }

                // 计算片元到圆锥轴线的垂直距离(径向距离)
                float3 radialVec = pointToTip - axialDistance * coneAxis;
                float radialDistance = length(radialVec);

                // 计算当前轴向位置允许的最大径向距离(圆锥的边缘)
                float halfAngleRad = radians(_ConeHalfAngleDeg);
                float maxRadialDistance = axialDistance * tan(halfAngleRad);

                // 如果径向距离超过圆锥边缘,丢弃像素
                if (radialDistance > maxRadialDistance)
                {
                    discard;
                }

                // 正常渲染物体的材质(保留原有的PBR属性)
                half4 albedo = tex2D(_MainTex, input.uv) * _BaseColor;
                half3 normal = normalize(input.normalWS);
                Light mainLight = GetMainLight();
                half3 diffuse = albedo.rgb * mainLight.color * saturate(dot(normal, mainLight.direction));
                half3 specular = mainLight.color * pow(saturate(dot(normalize(mainLight.direction + normalize(_WorldSpaceCameraPos - input.positionWS)), normal)), _Glossiness * 128) * _Metallic;
                half3 color = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb * albedo.rgb;

                return half4(color, albedo.a);
            }
            ENDHLSL
        }
    }
    FallBack "Universal Render Pipeline/Lit"
}

2. C# 控制脚本(实时更新圆锥参数)

这个脚本用来把圆锥的位置、方向等参数实时传递给Shader,确保每帧都能同步圆锥的变化:

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class ConeIntersectionController : MonoBehaviour
{
    public Transform coneTipTransform; // 用一个空物体来控制圆锥的顶点位置和旋转
    public Vector3 coneLocalAxis = Vector3.forward; // 圆锥轴线在coneTipTransform本地空间的方向
    public float coneHalfAngle = 45f;
    public float coneMaxLength = 1000f; // 足够大覆盖你的场景即可

    private Renderer _targetRenderer;
    private MaterialPropertyBlock _propBlock;

    void Awake()
    {
        _targetRenderer = GetComponent<Renderer>();
        _propBlock = new MaterialPropertyBlock();
    }

    void Update()
    {
        // 实时更新Shader的参数
        _targetRenderer.GetPropertyBlock(_propBlock);
        
        // 传递圆锥顶点的世界位置
        _propBlock.SetVector("_ConeTipWorldPos", coneTipTransform.position);
        // 把本地轴线方向转换为世界空间方向
        Vector3 worldAxis = coneTipTransform.TransformDirection(coneLocalAxis).normalized;
        _propBlock.SetVector("_ConeAxisWorldDir", worldAxis);
        _propBlock.SetFloat("_ConeHalfAngleDeg", coneHalfAngle);
        _propBlock.SetFloat("_ConeMaxLength", coneMaxLength);

        _targetRenderer.SetPropertyBlock(_propBlock);
    }
}

使用步骤

  1. 创建一个空物体,命名为ConeTip,用来控制圆锥的顶点位置和轴线方向(旋转这个空物体就能改变圆锥的朝向)
  2. 把上面的Shader创建为Material,赋值给需要显示相交效果的3D物体
  3. 把C#脚本挂载到该3D物体上,在Inspector里指定coneTipTransform为刚才创建的ConeTip空物体
  4. 调整圆锥的半角、最大长度等参数,切换到正交相机运行,就能看到实时的相交遮罩效果了

额外提示

  • 如果需要双向圆锥(比如顶点在中间,向两个方向延伸),只需要修改Shader里的axialDistance判断,去掉axialDistance < 0的条件,然后把axialDistance改成abs(axialDistance)即可
  • 因为圆锥尺寸很大,coneMaxLength设为场景最大尺寸的2倍就足够了,不用担心遗漏物体
  • 这个方案完全不需要延迟渲染,所有计算都在正向渲染的片元阶段完成,性能非常优秀

内容的提问来源于stack exchange,提问作者Chae Ho Shin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:39:57