在Unity中将3D对象渲染在2D对象后面

3
我正在制作一个由2D和3D元素组合而成的等轴测游戏。我想知道如何使我的玩家和敌人精灵(全部由2D元素组成)不会在我的3D元素后面或里面渲染。

enter image description here

所以我已经尝试过通过更改renderQueue并将这些岩石等材质的渲染队列设置为高值,使它们绘制在2D元素后面。
然而,在我的搜索中,我发现实际上需要设置对象的ztest来纠正这个问题。这让我有点困惑,因为我没有真正使用过着色器。我找不到任何合适的参考资料,解释如何准确地解决这个问题,只有一些假定先前知识的写作,比如this。我尝试下载Unity着色器并打开默认的精灵着色器,但我不确定在哪里更改ztest或标记来解决这个问题。
以下是Unity的标准精灵着色器:
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

#ifndef UNITY_SPRITES_INCLUDED
#define UNITY_SPRITES_INCLUDED

#include "UnityCG.cginc"

#ifdef UNITY_INSTANCING_ENABLED

UNITY_INSTANCING_BUFFER_START(PerDrawSprite)
    // SpriteRenderer.Color while Non-Batched/Instanced.
    UNITY_DEFINE_INSTANCED_PROP(fixed4, unity_SpriteRendererColorArray)
    // this could be smaller but that's how bit each entry is regardless of type
    UNITY_DEFINE_INSTANCED_PROP(fixed2, unity_SpriteFlipArray)
UNITY_INSTANCING_BUFFER_END(PerDrawSprite)

#define _RendererColor  UNITY_ACCESS_INSTANCED_PROP(PerDrawSprite, unity_SpriteRendererColorArray)
#define _Flip           UNITY_ACCESS_INSTANCED_PROP(PerDrawSprite,    unity_SpriteFlipArray)

#endif // instancing

CBUFFER_START(UnityPerDrawSprite)
#ifndef UNITY_INSTANCING_ENABLED
fixed4 _RendererColor;
fixed2 _Flip;
#endif
float _EnableExternalAlpha;
CBUFFER_END

// Material Color.
fixed4 _Color;

struct appdata_t
{
float4 vertex   : POSITION;
float4 color    : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct v2f
{
float4 vertex   : SV_POSITION;
fixed4 color    : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};

inline float4 UnityFlipSprite(in float3 pos, in fixed2 flip)
{
return float4(pos.xy * flip, pos.z, 1.0);
}

v2f SpriteVert(appdata_t IN)
{
v2f OUT;

UNITY_SETUP_INSTANCE_ID (IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

OUT.vertex = UnityFlipSprite(IN.vertex, _Flip);
OUT.vertex = UnityObjectToClipPos(OUT.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color * _RendererColor;

#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif

return OUT;
}

sampler2D _MainTex;
sampler2D _AlphaTex;

fixed4 SampleSpriteTexture (float2 uv)
{
fixed4 color = tex2D (_MainTex, uv);

#if ETC1_EXTERNAL_ALPHA
fixed4 alpha = tex2D (_AlphaTex, uv);
color.a = lerp (color.a, alpha.r, _EnableExternalAlpha);
#endif

  return color;
}

fixed4 SpriteFrag(v2f IN) : SV_Target
{
   fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
   c.rgb *= c.a;
   return c;
 }

#endif // UNITY_SPRITES_INCLUDED

更新 1

enter image description here


你是否尝试过将2D元素在3D空间中定位,就像你的3D元素一样,通过它们的变换位置来放置在需要在前面/后面的位置? - undefined
在我的情况下,这不是一个好主意。因为我的相机是等轴测的,所以它以41度的角度俯视着世界。将2D元素放在3D元素的“前面”意味着它们会以奇怪的角度放置,这使得瞄准系统和2D元素的位置都变得扭曲和混乱。我相当确定正确的做法是能够告诉某些3D元素它们应该简单地在2D元素后面渲染。我只是不知道如何编写一个能实现这一点的着色器。更新1说明了游戏中的效果。精灵在砖块前面渲染,但不在岩石前面渲染。 - undefined
你能用另一个相机吗?如果可以的话,你可以设置另一个相机的剔除掩码和深度。这样可以使得在该层中的某些物体显示在前面。 - undefined
着色器可以做到这一点,但我认为使用另一个相机会更容易实现。 - undefined
1个回答

2
如果我正确理解了您的问题,我认为您可以尝试使用额外的相机。
正确设置两个相机的裁剪掩码、深度和清除标志。
额外相机的深度应该更高,并将清除标志设置为仅深度,裁剪掩码仅设置您想要呈现在前面的层。
您原来的相机裁剪掩码应删除在额外相机中呈现的层。
您可以使特定层的对象呈现在前面。
昨天Svp告诉我他想要精细控制2D和3D对象的绘制顺序。我的旧答案无法很好地解决这个问题。所以经过研究,有三种解决方案。
更新:
  1. Treat 2d object as 3d. Use z-pos to sort all objects. Keep few 3d object z-pos always less than or greater than player z-pos (The offset of z-pos of 3d object and player need greater than fixed value that make the entire 3d object behind the player.).The fixed value could select 3d object bounds max.

  2. Treat 3d object as 2d. 3d object mesh render use 2d sprite-default shader. Use 2d sorting layer and order in layer control 3d object render.But we need custom mesh render inspector use follow code copy from here by uvivagabond.

    using System;
    using UnityEngine;
    using UnityEditor;
    using UnityEditorInternal;
    using System.Reflection;
    using UnityEngine.Rendering;
    [CanEditMultipleObjects ()]
    [CustomEditor (typeof(MeshRenderer))]
    public class MeshRendererSortingLayersEditor : Editor
    {
    
        public override void OnInspectorGUI ()
        {
    
            #region Get Serialized Property
            SerializedProperty sortingLayerID = serializedObject.FindProperty (propertyPath: "m_SortingLayerID");
            SerializedProperty sortingOrder = serializedObject.FindProperty ("m_SortingOrder");
    
            SerializedProperty castShadows = serializedObject.FindProperty ("m_CastShadows");
            SerializedProperty receiveShadows = serializedObject.FindProperty ("m_ReceiveShadows");
            SerializedProperty motionVectors = serializedObject.FindProperty ("m_MotionVectors");
            SerializedProperty materials = serializedObject.FindProperty ("m_Materials");
            SerializedProperty lightProbes = serializedObject.FindProperty ("m_LightProbeUsage");
            SerializedProperty reflectionProbes = serializedObject.FindProperty ("m_ReflectionProbeUsage");
            SerializedProperty anchorProbes = serializedObject.FindProperty ("m_ProbeAnchor");
            #endregion
    
            #region Draw Properties
            AddPropertyField (castShadows);
            AddPropertyField (receiveShadows);
            AddPropertyField (motionVectors);
            AddPropertyField (materials);
            AddPopup (ref lightProbes, "Light Probes", typeof(LightProbeUsage));
            AddPopup (ref reflectionProbes, "Reflection Probes", typeof(ReflectionProbeUsage));
            AddPropertyField (anchorProbes, "Anchor Override");
            #endregion
    
    
            GUIStyle style = new GUIStyle (GUI.skin.label);
            style.richText = true;
            EditorGUILayout.Space ();
            EditorGUILayout.LabelField ("<b><color=#EE4035FF>SortingLayers Options:</color></b>", style);
            #region SortingLayer
            Rect firstHoriz = EditorGUILayout.BeginHorizontal ();
            EditorGUI.BeginChangeCheck ();
            //    EditorGUI.PropertyField (mat, new GUIContent ("Materials"));
            EditorGUI.BeginProperty (firstHoriz, GUIContent.none, sortingLayerID);
            string[] layerNames = GetSortingLayerNames ();
            int[] layerID = GetSortingLayerUniqueIDs ();
            int selected = -1;
            int sID = sortingLayerID.intValue;
            for (int i = 0; i < layerID.Length; i++)
                if (sID == layerID [i])
                    selected = i;
            if (selected == -1)
                for (int i = 0; i < layerID.Length; i++)
                    if (layerID [i] == 0)
                        selected = i;
            selected = EditorGUILayout.Popup ("Sorting Layer", selected, layerNames);
    
            sortingLayerID.intValue = layerID [selected];
            EditorGUI.EndProperty ();
            EditorGUILayout.EndHorizontal ();
            #endregion
    
            #region OrderInLayer
            EditorGUILayout.BeginHorizontal ();
            EditorGUI.BeginChangeCheck ();
            EditorGUILayout.PropertyField (sortingOrder, new GUIContent ("Order in Layer"));
            EditorGUILayout.EndHorizontal ();
            serializedObject.ApplyModifiedProperties ();
            #endregion
    
    
        }
    
        void AddPropertyField (SerializedProperty ourSerializedProperty)
        {
            Rect ourRect = EditorGUILayout.BeginHorizontal ();
            EditorGUI.BeginProperty (ourRect, GUIContent.none, ourSerializedProperty);
            EditorGUI.BeginChangeCheck ();
    
            EditorGUILayout.PropertyField (property: ourSerializedProperty, includeChildren: true); //I set includeChildren:true to display material children
    
            EditorGUI.EndProperty ();
            EditorGUILayout.EndHorizontal ();
        }
    
        void AddPropertyField (SerializedProperty ourSerializedProperty, string name)
        {
            Rect ourRect = EditorGUILayout.BeginHorizontal ();
            EditorGUI.BeginProperty (ourRect, GUIContent.none, ourSerializedProperty);
            EditorGUI.BeginChangeCheck ();
    
            EditorGUILayout.PropertyField (ourSerializedProperty, new GUIContent (name), true);
    
            EditorGUI.EndProperty ();
            EditorGUILayout.EndHorizontal ();
        }
    
        void AddPopup (ref SerializedProperty ourSerializedProperty, string nameOfLabel, Type typeOfEnum)
        {
            Rect ourRect = EditorGUILayout.BeginHorizontal ();
            EditorGUI.BeginProperty (ourRect, GUIContent.none, ourSerializedProperty);
            EditorGUI.BeginChangeCheck ();
    
            int actualSelected = 1;  
            int selectionFromInspector = ourSerializedProperty.intValue;
            string[] enumNamesList = System.Enum.GetNames (typeOfEnum);
            actualSelected = EditorGUILayout.Popup (nameOfLabel, selectionFromInspector, enumNamesList);
            ourSerializedProperty.intValue = actualSelected;
    
            EditorGUI.EndProperty ();
            EditorGUILayout.EndHorizontal ();
        }
    
    
        public string[] GetSortingLayerNames ()
        {
            Type internalEditorUtilityType = typeof(InternalEditorUtility);
            PropertyInfo sortingLayersProperty = internalEditorUtilityType.GetProperty ("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic);
            return (string[])sortingLayersProperty.GetValue (null, new object[0]);
        }
    
        public int[] GetSortingLayerUniqueIDs ()
        {
            Type internalEditorUtilityType = typeof(InternalEditorUtility);
            PropertyInfo sortingLayerUniqueIDsProperty = internalEditorUtilityType.GetProperty ("sortingLayerUniqueIDs", BindingFlags.Static | BindingFlags.NonPublic);
            return (int[])sortingLayerUniqueIDsProperty.GetValue (null, new object[0]);
        }
    }
    
在此之后,您的渲染网格检查器视图如下所示: Mesh Render Inspector 3. 结合2D渲染系统和3D渲染系统。处理Z缓冲区、Z写入等诸多问题……我认为这是一个巨大的挑战。Unity应该做这个工作,而不是我们自己。
希望这能帮到您。

1
啊,我明白了。当然,我可以将所有我希望在玩家前面绘制的图层都添加进去,只是需要对元素进行一些重组,这样应该就可以了。 - undefined
@Svp额外的相机应该只将剔除掩码设置为你想要在前面的特定层。其他相机应该从剔除掩码中删除这个层。 - undefined
是的,但一旦我决定将玩家显示在石头前面,那就意味着玩家会显示在所有物体的前面,因为摄像机的深度更高。这就意味着我还需要将所有其他物体放入该摄像机中。过了一段时间,我在“前”摄像机中有了大量的图层,变得非常混乱。是的,对于我添加到前方的每个图层,我都会从后方移除它。事情开始变得混乱不堪。 - undefined
你可以反转它吗?将较少的内容放在后面渲染。 - undefined
看起来你的设计有时候A渲染在前面,有时候A渲染在后面。所以你需要在运行时改变图层。 - undefined
让我们在聊天中继续这个讨论 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接