Unity插件使用(2)_剖切插件CrossSection(HDRP)

目录

一、插件使用

二、修改增加动态切换的功能

三、包围盒移动问题

四、合并项目中后剖切的效果错误问题

五、剖切面贴图


插件默认是Build-in,需要打开HDRP包,2.7版本才开始支持HDRP,有个crossSection(HDRP)和crossSection(URP&HDRP),(URP&HDRP)是用shadergraph的

一、插件使用

1.创建一个HDRP项目

2.加载crossSection 2.7.unitypackage(淘宝上购买的,注意有些老的版本不知道HDRP的2.7开始才支持的)

3.在Unity中开crossSection(HDRP)和crossSection(URP&HDRP)

4.修改AutoLoadPipelineAsset,原本的代码是对应于URP的,HDRP下相应的改一下

using UnityEngine;
using UnityEngine.Rendering;

[ExecuteAlways]
public class AutoLoadPipelineAsset : MonoBehaviour
{
    public RenderPipelineAsset pipelineAsset;
    
    private void OnEnable()
    {
        UpdatePipeline();
    }

    void UpdatePipeline()
    {
        //if (pipelineAsset)
        //{
            GraphicsSettings.renderPipelineAsset = pipelineAsset;
        //}
    }
}

5.可以运行测试场景了,打开hdrp相关场景,6个场景前4个完全正常,

最后2个有点问题

二、修改增加动态切换的功能

剖切的功能实际上是通过Shader实现的,对于项目中的模型来说,就是切换模型材质的Shader,创建剖切平面。

1.切换材质Shader

    [ContextMenu("ReplaceMaterialShader")]
    void ReplaceMaterialShader(){
        if(csShader==null)
        {
            return;
        }
        foreach(var mat in hdrpMaterials){
            if(!shaderSaved.ContainsKey(mat)){
                shaderSaved.Add(mat,mat.shader);
            }
            //获取HDRP/Lit的属性
            var texture=mat.GetTexture("_BaseColorMap");
            var color=mat.GetColor("_BaseColor");
            color=new Color(color.r*ColorPower,color.g*ColorPower,color.b*ColorPower,color.a);
            //原来的颜色作为自发光颜色有点太亮了,乘于一个系数,暗一点。
            var smoothness=mat.GetFloat("_Smoothness");
            var metallic=mat.GetFloat("_Metallic");
            var scale=mat.GetTextureScale("_BaseColorMap");
            var offset=mat.GetTextureOffset("_BaseColorMap");

            mat.shader=csShader;//修改材质,csShader选择CrossSectionGraph/PBR_Box先,需要法线什么的要考虑其他Shader

            //设置新Shader的属性
            mat.SetColor("_SectionColor",color);
            mat.SetColor("_EmissionColor",color);
            mat.SetTexture("_MainTex",texture);
            mat.SetFloat("_metallic",metallic);
            mat.SetFloat("_smoothness",smoothness);
            mat.SetVector("_UvOffset",offset);
            mat.SetVector("_UvTiling",scale);
            mat.SetFloat("__retractBackfaces",1);
            mat.SetFloat("_inverse",0);
            mat.SetFloat("_oneSided",0);
        }
    }

    [ContextMenu("RecoverMaterialShader")]
    void RecoverMaterialShader(){
        foreach(var mat in materials){
            if(shaderSaved.ContainsKey(mat)){
                mat.shader=shaderSaved[mat];
            }
        }
        shaderSaved.Clear();
    }

2.修改剖切平面

修改剖切平面,包围住新的要剖切的物体。

SectionSetup里面已经提供了一个函数SetModel,调用一下就好了

3.隐藏其他物体

public List<GameObject> hideObjects=new List<GameObject>();
    public List<Renderer> targetRenders=new List<Renderer>();
    public List<Renderer> allRenderers=new List<Renderer>();

    [ContextMenu("HideOthers")]
    public void HideOthers(){
        ShowOthers();
        //hideObjects.Clear();
        targetRenders.Clear();
        targetRenders.AddRange(setup.model.GetComponentsInChildren<Renderer>());//剖切目标模型
        targetRenders.AddRange(setup.gameObject.GetComponentsInChildren<Renderer>());//剖切包围盒的面
        if(Environment!=null){
            targetRenders.AddRange(Environment.GetComponentsInChildren<Renderer>());//地面等环境模型
        }
        allRenderers=GameObject.FindObjectsOfType<Renderer>().ToList();
        foreach(var renderer in targetRenders){
            renderer.gameObject.SetActive(true);
        }
        foreach(var renderer in allRenderers){
            if(!targetRenders.Contains(renderer))
            {
                renderer.gameObject.SetActive(false);
                hideObjects.Add(renderer.gameObject);
            }
        }
    }

    [ContextMenu("ShowOthers")]
    public void ShowOthers(){
        foreach(var obj in hideObjects){
            obj.SetActive(true);
        }
        hideObjects.Clear();
    }

三、包围盒移动问题

合并到项目中后,发现包围盒的控制出问题了,明明选中的是一个面(+Z)拖动时移动的是另一个面(-X),

就是我明明要拖动前面的一个面,结果被判断为拖动后面的面了。

看代码,发现它的算法就是调整包围盒(CrossSectionBox)的比例和位置,最终达到拖动一个包围盒的平面的效果。

 

原来的判断代码:

                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                if (xAxis.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.X;
                    dragplane.SetNormalAndPosition(transform.up, transform.position);
                }
                else if (xAxisNeg.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.Xneg;
                    dragplane.SetNormalAndPosition(-transform.up, transform.position);
                }
                else if (yAxis.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.Y;
                    dragplane.SetNormalAndPosition(transform.forward, transform.position);
                }
                else if (yAxisNeg.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.Yneg;
                    dragplane.SetNormalAndPosition(-transform.forward, transform.position);
                }
                else if (zAxis.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.Z;
                    dragplane.SetNormalAndPosition(transform.up, transform.position);
                }
                else if (zAxisNeg.Raycast(ray, out hit, 1000f))
                {
                    selectedAxis = GizmoAxis.Zneg;
                    dragplane.SetNormalAndPosition(-transform.up, transform.position);
                }
                else
                {
                    //Debug.Log(hit.collider.name);
                    return;
                }

我加了个

                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                var hits=Physics.RaycastAll(ray);
                hitObjs.Clear();
                foreach(var h in hits){
                    hitObjs.Add(h.collider.gameObject);
                }

在独立的测试项目中,hitObjs只能打中前面的平面

而在工作项目中,前后平面都能打到,奇怪不奇怪....

平面本身是个Quad,没怎么用过....

它用if else的方式硬生生的导致有些面判断错误了....

不明白干嘛不用Physics.Raycast....

反正我改成用Physics.Raycast判断了,问题解决了。

                RaycastHit hit;
                if(Physics.Raycast(ray,out hit)){
                    hitCollider=hit.collider;
                    if (xAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.X;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                    }
                    else if (xAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Xneg;
                        dragplane.SetNormalAndPosition(-transform.up, transform.position);
                    }
                    else if (yAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Y;
                        dragplane.SetNormalAndPosition(transform.forward, transform.position);
                    }
                    else if (yAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Yneg;
                        dragplane.SetNormalAndPosition(-transform.forward, transform.position);
                    }
                    else if (zAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Z;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                    }
                    else if (zAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Zneg;
                        dragplane.SetNormalAndPosition(-transform.up, transform.position);
                    }
                    else
                    {
                        //Debug.Log(hit.collider.name);
                        return;
                    }
                }
                else{
                    return;
                }

发现在这种情况下无法点到包围平面

再调整一下吧,用RaycastAll

                ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                var hits=Physics.RaycastAll(ray);
                if(hits.Length==0) return;
                hitObjs.Clear();
                selectedAxis=GizmoAxis.none;
                RaycastHit hit=new RaycastHit();
                foreach(var h in hits){
                    hit=h;
                    hitObjs.Add(h.collider.gameObject);

                    hitCollider=h.collider;
                    if (xAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.X;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                        break;
                    }
                    else if (xAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Xneg;
                        dragplane.SetNormalAndPosition(-transform.up, transform.position);
                        break;
                    }
                    else if (yAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Y;
                        dragplane.SetNormalAndPosition(transform.forward, transform.position);
                        break;
                    }
                    else if (yAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Yneg;
                        dragplane.SetNormalAndPosition(-transform.forward, transform.position);
                        break;
                    }
                    else if (zAxis == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Z;
                        dragplane.SetNormalAndPosition(transform.up, transform.position);
                        break;
                    }
                    else if (zAxisNeg == hitCollider)
                    {
                        selectedAxis = GizmoAxis.Zneg;
                        dragplane.SetNormalAndPosition(-transform.up, transform.position);
                        break;
                    }
                }
                if(selectedAxis==GizmoAxis.none)return;

当然,设置一个Layer的话,上面的问题就没有了,但是这不是 可以不用设置Layer嘛。

合并到项目中发现还是有问题,在合并到项目中后,碰撞到的剖切面顺序好像不是按照一定的规则的....,反正明明在前面的平面不是第一个。

那,只能加个Layer了。

----------------------------------------------------------

四、合并项目中后剖切的效果错误问题

每个面都差了一段距离。

剖切区域跟随包围盒是受脚本CappedSectionFollow控制的,里面有

        void OnEnable()
        {
            if (sectionMode == Mode.box)
            {
                Shader.DisableKeyword("CLIP_NONE");
                Shader.EnableKeyword("CLIP_BOX");
                //Shader.SetGlobalInt("_CLIP_BOX", 1);
            }
            if (sectionMode == Mode.corner)
            {
                Shader.DisableKeyword("CLIP_NONE");
                Shader.EnableKeyword("CLIP_CORNER");
                //Shader.SetGlobalInt("_CLIP_CORNER", 1);
            }
            SetSection();
        }

而我的项目似乎是材质太多了

Maximum number (256) of shader global keywords exceeded, keyword CLIP_NONE will be ignored.

解决方案,将keyword改为local。

参考1,讨论:https://forum.unity.com/threads/maximum-number-256-of-shader-global-keywords-exceeded-keyword-will-be-ignored-and-black-materials.803613/

参考2,调查:https://forum.unity.com/threads/shader-keyword-limit.545491/#post-3832954

参考3,例子:https://docs.google.com/document/d/156MgqojKIgWgrpJLJ0WgD8pVzG0A0T42e3wr0d0oaLg/edit

 
#pragma multi_compile_local _ FOO_ON

//This will result in two variants with local keyword FOO_ON.

public Material mat;
…
Private void Start()
{
    mat.EnableKeyword(“FOO_ON”);
}
...

//Local keyword can be enabled the same way like before.

参考4,文档:https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html?_ga=2.105567555.1551600638.1607676658-60239732.1590568025

Enabling and disabling shader keywords
To enable and disable shader keywords, use the following APIs:

Shader.EnableKeyword: enable a global keyword
Shader.DisableKeyword: disable a global keyword
CommandBuffer.EnableShaderKeyword: use a CommandBuffer to enable a global keyword
CommandBuffer.DisableShaderKeyword: use a CommandBuffer to disable a global keyword
Material.EnableKeyword: enable a local keyword for a regular shader
Material.DisableKeyword: disable a local keyword for a regular shader
ComputeShader.EnableKeyword: enable a local keyword for a compute shader
ComputeShader.DisableKeyword: disable a local keyword for a compute shader
When you enable or disable a keyword, Unity uses the appropriate variant.

修改ShaderGraph的Globle:

 

修改其他材质的Shader,CapWrite.shader

	//#pragma multi_compile __ CLIP_BOX CLIP_CORNER CLIP_PLANE CLIP_SPHERE_OUT
    #pragma multi_compile_local _ CLIP_BOX CLIP_CORNER CLIP_PLANE CLIP_SPHERE_OUT

加载相关的材质,

最后设置Keyword

        public void DisableKeyword(string key){
            Debug.Log("CappedSectionFollow.DisableKeyword:"+key);
            if(IsLocalKeyword==false){
                Shader.DisableKeyword(key);
            }
            else{
                foreach(var mat in modelMaterials)
                {
                    mat.DisableKeyword(key);
                }
                foreach(var mat in sectionMaterials)
                {
                    mat.DisableKeyword(key);
                }
            }
        }

        public void EnableKeyword(string key){
            Debug.Log("CappedSectionFollow.EnableKeyword:"+key);
             if(IsLocalKeyword==false){
                Shader.EnableKeyword(key);
            }
            else{
                if(setup==null){
                    setup=GameObject.FindObjectOfType<SectionSetup>();
                }
                if(setup.model!=lastSetupModel){
                    lastSetupModel=setup.model;
                }
                GetMatierals(setup.model);

                foreach(var mat in modelMaterials)
                {
                    mat.EnableKeyword(key);
                }
                foreach(var mat in sectionMaterials)
                {
                    mat.EnableKeyword(key);
                }
            }
        }

最终,单独测试,和原本的功能不变。

合并到项目中,剖切效果正常了,但是打印里面还是有那个

Maximum number (256) of shader global keywords exceeded, keyword CLIP_NONE will be ignored.

奇怪....

另外_MAPPING_TRIPLANAR,_MASKMAP,_Emission,几个local的keyword设置时也是会提示错误的,但是效果正常。

五、剖切面贴图

剖面效果受几个方面影响

1.模型材质上的SectionColor,这是最基本的,我一般设置成和主颜色一致。

没有将物体的layer设置为下面的CustomWritePass上的LayerMask的情况下显示的就是这个颜色,或者直接CustomWritePass没有时。

注意oneSided必须是false,不然会是直接透过的

透过效果

2.CustomeWritePass上的CapWrite材质上的StencilMask的数值影响,1-63,64-127,128-191,192-255,4个类别。

1-63:SectionColor颜色

64-127:纹理1

128-191:纹理2

192-255:纹理3

学习一下StencilMask吧。。。

 

3.hatch平面,将CrossSectionBox里面的x_hatch平面去掉则纹理不显示

Hatch上有三个材质

三种切面的效果就是这三种材质的

总之,

1.模型上需要使用CrossSectionGraph/PBR_Box Shader 

2.需要有CrawRenderersCustomPass,使用CrossSectionHDRP/Renderers/CapWrite Shader ,

3.有个x_patch平面,使用 CrossSectionHDRP/StencilShow Shader。

1是剖切的核心 2+3做出剖切面的纹理效果。

说实话,没学过理解这些效果原理的知识啊!!!!!

----------------------------------------------------------------------------------------------------------

3.项目中使用时,没有设置layer,

设置了Layer

总之无法得到实心的剖切效果。

虽然拖出来,独立一块,不要和其他模型重叠在一起是,剖切效果会好一点,比较“实心”,但是剖切面会闪面,有时候显示的不是剖切面

纯色切面的话倒是看不大出来问题

不考虑有纹理的剖切面的闪烁问题的话,剩下的注意问题就是,重叠模型导致的“非实心”效果,CrossSectionGraph/PBR_Box

几种材质切换测试,发现CrossSectionGraph/Autodesk Interactive效果比CrossSectionGraph/PBR_Box好一点点

onesiede:true的话

两者方式如果结合起来姐好了

或者从模型开始处理,让模型不相互重叠,其实也就是我要的剖切效果了,但是模型的数量很大

现在渲染2个模型的话大概有4个部分,模型1正面,模型2正面,模型1反面,模型2反面,正面渲染的优先级高,也就是先渲染的反面再渲染的正面。两个物体的正面之间又是根据深度来觉得渲染顺序的。

没有剖切前是正常的,红色的正面最终都会挡住灰色的正面,

剖切后先渲染了灰色的反面和红色的反面,然后再渲染了灰色的正面和红色正面,结果就是,灰色的反面挡住红色的反面,灰色的正面挡住了红色的反面,应该一片红的区域出现了灰色。

如果剖面效果能够渲染在最后面,将这些都挡住的话,在重叠部分无法是哪个模型的剖切纹理在前,都不会出现这种空心的效果

但实际上看来,纹理是渲染在模型的反面上的,效果还是穿插了的。甚至,它这个渲染还不稳定!

............................对于我当前没有渲染知识的人来说,要做出合理的剖切效果,只能是纯色切面+处理模型不重叠,暂时。

演示项目其实如果模型重叠在一起也不会那么和谐的,所以呢,我还是处理模型吧。

---------------------------------------------------再挣扎一下

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值