赞
踩
目的:
实现环境:Unity(个人免费,白飘谁不飘)
效果如下:
看着有点恶心,:P,跟毛没刮干净一样,特别是着黑色时
左侧球,Geometry Shader
左侧正方体,Geometry Shader
右侧正方体,Mesh实现
右侧胶囊,Mesh实现
两个正方体作为对比,效果一样
Geometry Shader
核心想法是增加Geometry Shader接管几何化部分,改变原三角形几何结构,输出线段流。
与传统Vertex Shader输出后供Fragment Shader插值不同,当增加Geometry Shader后,Vertex Shader输出供Geometry Shader几何化,然后Geometry Shader再输出供Fragment Shader插值。Geometry Shader在这里的作用是改变(增删改都可以)几何(拓扑)结构。与此同时,数据流的定义也与以往不同,详细见下。
Vertex Shader
接收应用输入,包括顶点坐标vertex,切向量tangent,法向量normal,然后平推给Geometry Shader。这里假设都已经归一化过。这是让人不爽的地方,啥事都没干。
Geometry Shader
以输入三角形质心为原点,构建切线空间坐标轴,并给每个轴赋予不同颜色以示区别,把原本三角形输出变成线段流(也可以改成LineStrip,但着色不方便了,就好比Cube同一个点在不同面上为不同顶点一样)
质心为
float4 vCentroid = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3;
法线、切线全部相加后归一化(除不除3不影响归一化,假设输入是归一化后的)
fixed3 normal = normalize(IN[0].normal + IN[1].normal + IN[2].normal);
fixed3 tangent = normalize(IN[0].tangent + IN[1].tangent + IN[2].tangent);
副切线为切线和法线的叉积(归一化后叉积仍未单位向量),这里先不讲究左右手性
fixed3 bitagnent = cross(tangent, normal);
各轴上另取一点就可以构成坐标轴,例如切线上,其他轴类推
vCentroid + tangent * _ScaleTangent
构建线段。每根线段为2个(坐标一样颜色不一样的算不同点),并将坐标转换成裁剪空间坐标送入插值器,例如切线坐标轴,其他轴类推
o.color = _ColorTangent;
o1.pos = UnityObjectToClipPos(vCentroid + tangent * _ScaleTangent);
o1.color = _ColorTangent;
linetream.Append(o);
linetream.Append(o1);
linetream.RestartStrip();
至此就大功告成了。
Fragment Shader
直接输出插值点颜色就可以,线段两顶点同色,所以怎么插都是线段颜色。
详细代码:
BoreasGeometryTangentSpace.shader
BoreasEffectGeometryTriangle.cs,挂载到模型上就可以
详细见后附件
此方法讨论
Mesh法
核心想法是遍历目标模型的每个三角面,直接构建一个新模型,原模型的每个面被线段构成的切线空间坐标轴替代。每次渲染完模型后,再渲染构建的新模型,从而只建模一次,效率极高。在这里,我们只关心模型,并不太关心材质相关,实际上只需对Mesh操作,并且是基于高层API,而不需要写Shader来操作,极大方便了划水。但对Mesh的数据结构要比较熟悉,操作一般都是基于索引。详细做法与Geometry Shader相同,需要注意的如下,
原模型三角形列表拿到的是顶点索引,而不是具体顶点,例如计算质心时如下,
int v0 = gameObjectMesh.triangles[i * 3];
int v1 = gameObjectMesh.triangles[i * 3 + 1];
int v2 = gameObjectMesh.triangles[i * 3 + 2];
Vector3 vertexOrigin = (gameObjectMesh.vertices[v0] + gameObjectMesh.vertices[v1] + gameObjectMesh.vertices[v2]) / 3.0f;
在构建坐标轴时,使用的是新模型的新索引,所以类似如下,
int idxVertexOrigin = vertices.Count;
lines.AddRange(new int[] {
idxVertexOrigin + 0, idxVertexOrigin + 1,
idxVertexOrigin + 2, idxVertexOrigin + 3,
idxVertexOrigin + 4, idxVertexOrigin + 5
});
详细代码:
BoreasGeometryTangentSpace.cs,建模+渲染
BoreasPointColor.shader,纯粹为了渲染,Unity自带Unlit/Color不支持逐顶点着色。
此方法讨论
这个话题至此就介绍完了。本质上平平无奇,打法时间划下水
BoreasGeometryTangentSpace.shader
Shader "Boreas/Geometry/TangentSpace" {
Properties {
_ColorTangent ("Color of Tangent", Color) = (1, 0, 0, 1)
_ScaleTangent ("Scale of Tangent", Range(0, 1)) = 0.1
_ColorBitangent ("Color of Bitangent", Color) = (0, 1, 0, 1)
_ScaleBitangent ("Scale of Bitangent", Range(0, 1)) = 0.1
_ColorNormal ("Color of Normal", Color) = (0, 0, 1, 1)
_ScaleNormal ("Scale of Normal", Range(0, 1)) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "TangentSpace"
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 tangent : TANGENT;
};
struct v2g {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 tangent : TANGENT;
};
struct g2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
fixed4 _ColorTangent;
fixed _ScaleTangent;
fixed4 _ColorBitangent;
fixed _ScaleBitangent;
fixed4 _ColorNormal;
fixed _ScaleNormal;
v2g vert (a2v v) {
v2g o;
o.vertex = v.vertex;
o.normal = v.normal;
o.tangent = v.tangent;
return o;
}
[maxvertexcount(6)]
void geom(triangle v2g IN[3], inout LineStream<g2f> linetream) {
g2f o, o1;
fixed3 normal = normalize(IN[0].normal + IN[1].normal + IN[2].normal);
fixed3 tangent = normalize(IN[0].tangent + IN[1].tangent + IN[2].tangent);
fixed3 bitagnent = cross(tangent, normal);
float4 vCentroid = (IN[0].vertex + IN[1].vertex + IN[2].vertex) / 3;
vCentroid.xyz += normal * 0.02;
o.pos = UnityObjectToClipPos(vCentroid);
o.color = _ColorTangent;
o1.pos = UnityObjectToClipPos(vCentroid + tangent * _ScaleTangent);
o1.color = _ColorTangent;
linetream.Append(o);
linetream.Append(o1);
linetream.RestartStrip();
o.color = _ColorBitangent;
o1.pos = UnityObjectToClipPos(vCentroid + bitagnent * _ScaleBitangent);
o1.color = _ColorBitangent;
linetream.Append(o);
linetream.Append(o1);
linetream.RestartStrip();
o.color = _ColorNormal;
o1.pos = UnityObjectToClipPos(vCentroid + normal * _ScaleNormal);
o1.color = _ColorNormal;
linetream.Append(o);
linetream.Append(o1);
linetream.RestartStrip();
}
fixed4 frag (g2f i) : SV_Target {
return i.color;
}
ENDCG
}
}
}
BoreasEffectGeometryTriangle.cs
using UnityEngine;
using UnityEngine.Rendering;
namespace BoreasGame {
[ExecuteAlways]
public class BoreasEffectGeometryTriangle : MonoBehaviour {
public Shader shader;
Material material;
CommandBuffer commandBuffer;
void Start() {
material = shader ? new Material(shader) : new Material(Shader.Find("Boreas/Geometry/TangentSpace"));
commandBuffer = new CommandBuffer() {name = "BoreasEffectNormal"};
foreach (Renderer renderer in gameObject.GetComponentsInChildren<Renderer>()) {
for (int i = 0; i < renderer.sharedMaterials.Length; i++) {
commandBuffer.DrawRenderer(renderer, material, i, 0);
}
}
}
void OnRenderObject() {
if (material != null && commandBuffer !=null) {
Graphics.ExecuteCommandBuffer(commandBuffer);
}
}
}
}
BoreasGeometryTangentSpace.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace BoreasGame {
public class BoreasGeometryTangentSpace : MonoBehaviour {
public Color colorTangent = Color.red;
[Range(0, 1)]
public float scaleTangent = 0.1f;
public Color colorBitangent = Color.green;
[Range(0, 1)]
public float scaleBitangent = 0.1f;
public Color colorNormal = Color.blue;
[Range(0, 1)]
public float scaleNormal = 0.1f;
Material material;
CommandBuffer commandBuffer;
GameObject effectObject;
void Start() {
CreateEffectObject();
effectObject.transform.position = gameObject.transform.position;
//material = new Material(Shader.Find("Unlit/Color"));
material = new Material(Shader.Find("Boreas/PointColor"));
commandBuffer = new CommandBuffer() {name = "BoreasEffectTangentSpace"};
foreach (Renderer renderer in effectObject.GetComponentsInChildren<Renderer>()) {
for (int i = 0; i < renderer.sharedMaterials.Length; i++) {
commandBuffer.DrawRenderer(renderer, material, i, 0);
}
}
}
void OnRenderObject() {
if (material != null && commandBuffer !=null) {
Graphics.ExecuteCommandBuffer(commandBuffer);
}
}
void CreateEffectObject() {
Mesh gameObjectMesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
effectObject = new GameObject(name, typeof(MeshFilter), typeof(MeshRenderer));
Mesh mesh = new Mesh() {name = name};
effectObject.GetComponent<MeshFilter>().sharedMesh = mesh;
List<Vector3> vertices = new List<Vector3>();
List<Color> colors = new List<Color>();
List<int> lines = new List<int>();
for (int i = 0; i < gameObjectMesh.triangles.Length / 3; i++) {
int v0 = gameObjectMesh.triangles[i * 3];
int v1 = gameObjectMesh.triangles[i * 3 + 1];
int v2 = gameObjectMesh.triangles[i * 3 + 2];
Vector3 tangent = (gameObjectMesh.tangents[v0] + gameObjectMesh.tangents[v1] + gameObjectMesh.tangents[v2]).normalized;
Vector3 normal = (gameObjectMesh.normals[v0] + gameObjectMesh.normals[v1] + gameObjectMesh.normals[v2]).normalized;
Vector3 bitangent = Vector3.Cross(tangent, normal);
Vector3 vertexOrigin = (gameObjectMesh.vertices[v0] + gameObjectMesh.vertices[v1] + gameObjectMesh.vertices[v2]) / 3.0f;
vertexOrigin += normal * 0.02f;
Vector3 vertexTangent = vertexOrigin + tangent * scaleTangent;
Vector3 vertexBitangent = vertexOrigin + bitangent * scaleBitangent;
Vector3 vertexNormal = vertexOrigin + normal * scaleNormal;
int idxVertexOrigin = vertices.Count;
vertices.Add(vertexOrigin); colors.Add(colorTangent);
vertices.Add(vertexTangent); colors.Add(colorTangent);
vertices.Add(vertexOrigin); colors.Add(colorBitangent);
vertices.Add(vertexBitangent); colors.Add(colorBitangent);
vertices.Add(vertexOrigin); colors.Add(colorNormal);
vertices.Add(vertexNormal); colors.Add(colorNormal);
lines.AddRange(new int[] {
idxVertexOrigin + 0, idxVertexOrigin + 1,
idxVertexOrigin + 2, idxVertexOrigin + 3,
idxVertexOrigin + 4, idxVertexOrigin + 5
});
}
mesh.SetVertices(vertices);
mesh.SetColors(colors);
mesh.SetIndices(lines, MeshTopology.Lines, 0);
}
}
}
BoreasPointColor.shader
Shader "Boreas/PointColor" {
Properties {
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 100
Pass {
Name "PointColor"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v {
float4 vertex : POSITION;
fixed4 color : COLOR;
};
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target {
return i.color;
}
ENDCG
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。