当前位置:网站首页>UnityShader-LowPoly
UnityShader-LowPoly
2022-07-27 06:14:00 【122&&113】
LowPoly
A low polygon style painting or effect .
Reference article :
【Unity Shader】 New book cover — Low Polygon Style rendering
Advanced graphics —— Surface subdivision and geometry shaders
The effect is as follows :


principle

From my personal understanding , The original normal is shown in the left figure above , In the slice shader phase , Will The three normals of a triangle are interpolated Get the normal of the current shading point , So as to produce a smooth effect , That's the picture on the right .
and LowPoly Style just doesn't need smoothing , On the contrary, I want the angular feeling of the left figure , So the core idea is that the current triangle uses only one normal , Let the normal used by the shading points of the triangle area be the same .
Shader
Shader "NPR/LowPolyStyle"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Pass{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM
#pragma multi_compile_fwdbase
#pragma target 4.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
struct v2g{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
struct g2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float3 faceNormal:TEXCOORD2;
};
v2g vert(appdata_base v){
v2g o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
return o;
}
[maxvertexcount(3)] // Used to define the maximum output point
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream){
// Define element input :point、 line、 lineadj、 triangle、 triangleadj Points, lines and surfaces
// Define element output :PointStream、 LineStream、 TriangleStream ditto
float3 A = IN[1].worldPos.xyz - IN[0].worldPos.xyz;
float3 B = IN[2].worldPos.xyz - IN[0].worldPos.xyz;
float3 fn = normalize(cross(A, B));
g2f o;
o.pos = IN[0].pos;
o.uv = IN[0].uv;
o.worldPos = IN[0].worldPos;
o.faceNormal = fn;
triStream.Append(o);
o.pos = IN[1].pos;
o.uv = IN[1].uv;
o.worldPos = IN[1].worldPos;
o.faceNormal = fn;
triStream.Append(o);
o.pos = IN[2].pos;
o.uv = IN[2].uv;
o.worldPos = IN[2].worldPos;
o.faceNormal = fn;
triStream.Append(o);
}
fixed4 frag(g2f i):SV_Target{
fixed3 lightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 normalDir = normalize(i.faceNormal);
fixed diff = saturate(dot(normalDir, lightDir));
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * diff;
return fixed4(diffuse + ambient, 1);
}
ENDCG
}
Pass {
Tags {"LightMode"="ForwardAdd"}
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#pragma target 4.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
struct v2g {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float4 _ShadowCoord : TEXCOORD2;
};
struct g2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float3 faceNormal : TEXCOORD2;
float4 _ShadowCoord : TEXCOORD3;
};
v2g vert(appdata_base v) {
v2g o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o._ShadowCoord = mul(unity_WorldToShadow[0], mul(unity_ObjectToWorld, v.vertex));
return o;
}
[maxvertexcount(3)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream) {
float3 A = IN[1].worldPos.xyz - IN[0].worldPos.xyz;
float3 B = IN[2].worldPos.xyz - IN[0].worldPos.xyz;
float3 fn = normalize(cross(A, B));
float3 worldPos = (IN[0].worldPos + IN[1].worldPos + IN[2].worldPos)/3.0;
g2f o;
o.pos = IN[0].pos;
o.uv = IN[0].uv;
o.worldPos = worldPos;
o.faceNormal = fn;
o._ShadowCoord = IN[0]._ShadowCoord;
triStream.Append(o);
o.pos = IN[1].pos;
o.uv = IN[1].uv;
o.worldPos = worldPos;
o.faceNormal = fn;
o._ShadowCoord = IN[1]._ShadowCoord;
triStream.Append(o);
o.pos = IN[2].pos;
o.uv = IN[2].uv;
o.worldPos = worldPos;
o.faceNormal = fn;
o._ShadowCoord = IN[2]._ShadowCoord;
triStream.Append(o);
}
fixed4 frag(g2f i) : SV_Target {
fixed3 lightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed3 normalDir = normalize(i.faceNormal);
fixed diff = saturate(dot(normalDir, lightDir));
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * diff * atten * atten;
return fixed4(diffuse, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
Focus on analyzing the code of geometric shaders :
[maxvertexcount(3)] // Used to define the maximum output point
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream){
// Define element input :point、 line、 lineadj、 triangle、 triangleadj Points, lines and surfaces
// Define element output :PointStream、 LineStream、 TriangleStream ditto
float3 A = IN[1].worldPos.xyz - IN[0].worldPos.xyz;
float3 B = IN[2].worldPos.xyz - IN[0].worldPos.xyz;
float3 fn = normalize(cross(A, B));
g2f o;
o.pos = IN[0].pos;
o.uv = IN[0].uv;
o.worldPos = IN[0].worldPos;
o.faceNormal = fn;
triStream.Append(o);
o.pos = IN[1].pos;
o.uv = IN[1].uv;
o.worldPos = IN[1].worldPos;
o.faceNormal = fn;
triStream.Append(o);
o.pos = IN[2].pos;
o.uv = IN[2].uv;
o.worldPos = IN[2].worldPos;
o.faceNormal = fn;
triStream.Append(o);
}
among A, B Is used to obtain the normal vector of the triangular patch , Here's the picture :

Three vertices are entered , The normal value of the output three vertices is the current calculated , Then the interpolation done before it is passed into the slice shader does not change the normal vector , Because the three normal vectors of interpolation are the same , How to interpolate is the same .
After getting the normal , The following code is the same as ordinary code , Generally speaking, the principle is relatively simple , But to get more interesting results , There should be a lot more trick, The road of learning is endless ~~~
边栏推荐
猜你喜欢
随机推荐
Dynamic programming for solving problems (1)
Solve binary tree (7)
基于C#的Winform对Access数据库进行操作(mdb结尾)
Linear progression for face recognition
力扣题解 动态规划(7)
Unity Hub登录无响应
[song] rebirth of me in py introduction training (5): List
C语言-动态内存管理
编程学习记录——第5课【分支和循环语句】
What is the difference between single line and three line when renting servers in Hong Kong?
力扣题解 动态规划(3)
wireshark数据包修改--添加或修改消息字段(二)
ROS节点名称重名
遥感影像识别-制作数据集
Li Kou daily question sword finger offer II 091. paint the house
软件测试用里篇
ROS运行管理之launch文件
[Arduino] reborn Arduino monk (1)
pycharm安装及导入项目注意事项
Remote sensing image recognition misclassification under multi class recognition









