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); } }
使用步骤
- 创建一个空物体,命名为
ConeTip,用来控制圆锥的顶点位置和轴线方向(旋转这个空物体就能改变圆锥的朝向) - 把上面的Shader创建为Material,赋值给需要显示相交效果的3D物体
- 把C#脚本挂载到该3D物体上,在Inspector里指定
coneTipTransform为刚才创建的ConeTip空物体 - 调整圆锥的半角、最大长度等参数,切换到正交相机运行,就能看到实时的相交遮罩效果了
额外提示
- 如果需要双向圆锥(比如顶点在中间,向两个方向延伸),只需要修改Shader里的
axialDistance判断,去掉axialDistance < 0的条件,然后把axialDistance改成abs(axialDistance)即可 - 因为圆锥尺寸很大,
coneMaxLength设为场景最大尺寸的2倍就足够了,不用担心遗漏物体 - 这个方案完全不需要延迟渲染,所有计算都在正向渲染的片元阶段完成,性能非常优秀
内容的提问来源于stack exchange,提问作者Chae Ho Shin
相关产品推荐
相关产品推荐

