目录
一、插件使用
二、修改增加动态切换的功能
三、包围盒移动问题
四、合并项目中后剖切的效果错误问题
五、剖切面贴图
插件默认是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。
参考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.
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反面,正面渲染的优先级高,也就是先渲染的反面再渲染的正面。两个物体的正面之间又是根据深度来觉得渲染顺序的。
没有剖切前是正常的,红色的正面最终都会挡住灰色的正面,
剖切后先渲染了灰色的反面和红色的反面,然后再渲染了灰色的正面和红色正面,结果就是,灰色的反面挡住红色的反面,灰色的正面挡住了红色的反面,应该一片红的区域出现了灰色。
如果剖面效果能够渲染在最后面,将这些都挡住的话,在重叠部分无法是哪个模型的剖切纹理在前,都不会出现这种空心的效果
但实际上看来,纹理是渲染在模型的反面上的,效果还是穿插了的。甚至,它这个渲染还不稳定!
............................对于我当前没有渲染知识的人来说,要做出合理的剖切效果,只能是纯色切面+处理模型不重叠,暂时。
演示项目其实如果模型重叠在一起也不会那么和谐的,所以呢,我还是处理模型吧。
---------------------------------------------------再挣扎一下