问题背景:
我们开发中需要有“点”对象,可以是像素的(不具备透视效果),始终等大,还有就是3D场景下的矢量点(随相机距离透视变化的)。
问题解决思路:
方案1:使用GS扩充顶点,并扩充三角面,通过GS直接绘制出想要图形点(我这里有几种图形的点)
方案2:使用GS扩充顶点,绘制出正方形面,到FS中去截取想要的形状
这两种方案基本思想都是一样的,我这里提供两种方案是因为我这个点的边框是有要求的,用GS扩线围一圈效果不好
知识扩展:
Geomotry Shader(几何着色器):几何着色器是可选的着色器,它位于顶点和片段着色器之间,它以一个或多个表示为一个单独基本图形(primitive)即图元的顶点作为输入,比如可以是一个点或者三角形,几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。几何着色器有意思的地方在于它可以把(一个或多个)顶点转变为完全不同的基本图形(primitive),从而生成比原来多得多的顶点。
上面是官方解释,通俗来讲:就是GeometryShader接收的实际上是VS输出的图元,对这些图元进行添加,修改,删除,然后输出新的图元信息。
图片说明:
这也就是人们常见的shader流程图。
具体实现代码:
方案一:
1 Shader "Vector/Point" 2 { 3 Properties 4 { 5 _Color("MianColor",color)=(1,1,1,1) 6 } 7 SubShader 8 { 9 Pass 10 { 11 Tags { "RenderType"="Opaque" } 12 LOD 100 13 Cull Off 14 CGPROGRAM 15 #pragma target 4.0 16 #pragma vertex vert 17 #pragma fragment frag 18 #pragma geometry geom 19 #include "UnityCG.cginc" 20 struct appdata 21 { 22 float4 vertex : POSITION; 23 float2 uv : TEXCOORD0; 24 float3 normal : NORMAL; 25 }; 26 struct v2g 27 { 28 float4 vertex : POSITION; 29 float3 nor:NORMAL; 30 }; 31 struct g2f 32 { 33 float4 vertex : SV_POSITION; 34 float3 norg:NORMAL; 35 }; 36 37 fixed4 _LightColor0; 38 fixed4 _Color; 39 float radius; 40 float initOffsetAngle=0; 41 float verticesNumber=3; 42 float IsEqualPixelPoint=0.0; 43 44 v2g vert(appdata_base v) 45 { 46 v2g o; 47 o.vertex = v.vertex; 48 o.nor=v.normal; 49 return o; 50 } 51 52 void ADD_VERTEX(float3 v,g2f o,inout TriangleStream<g2f> tristream) 53 { 54 o.vertex = UnityObjectToClipPos(v); 55 tristream.Append(o); 56 } 57 void ADD_INFO(float3 p0,float3 p1,float3 p2,g2f o,inout TriangleStream<g2f> tristream) 58 { 59 60 ADD_VERTEX(p0,o,tristream); 61 ADD_VERTEX(p1,o,tristream); 62 ADD_VERTEX(p2,o,tristream); 63 tristream.RestartStrip(); 64 } 65 66 // 根据模型坐标获得屏幕坐标 67 float4 GetScreenPos(float4 vertex) 68 { 69 float4 pos = UnityObjectToClipPos(vertex); 70 //return getScreenPosByClipPos(pos); 71 72 pos = ComputeScreenPos(pos); 73 pos.xy = pos.xy / pos.w; 74 pos.xy = pos.xy * _ScreenParams.xy; 75 return pos; 76 } 77 78 //将旋转完的坐标转回剪裁空间下 79 void ADD_VERTEX_Other(float4 v,g2f o,inout TriangleStream<g2f> tristream) 80 { 81 float4 pos=v; 82 pos.xy=v.xy/_ScreenParams.xy; 83 pos.xy=pos.xy * v.w; 84 85 pos.y= (pos.y-(0.5*pos.w))/(_ProjectionParams.x * 0.5); 86 pos.x=(pos.x-(0.5*pos.w))/0.5; 87 88 o.vertex = pos; 89 tristream.Append(o); 90 } 91 92 void ADD_INFO_Other(float4 p0,float4 p1,float4 p2,g2f o,inout TriangleStream<g2f> tristream) 93 { 94 ADD_VERTEX_Other(p0,o,tristream); 95 ADD_VERTEX_Other(p1,o,tristream); 96 ADD_VERTEX_Other(p2,o,tristream); 97 tristream.RestartStrip(); 98 } 99 100 [maxvertexcount(60)] 101 void geom(triangle v2g IN[3], inout TriangleStream<g2f> tristream) 102 { 103 g2f o; 104 if(IsEqualPixelPoint==0.0) 105 { 106 float3 p0=float3(0,0,0); 107 float3 dir=normalize( mul(unity_WorldToObject,_WorldSpaceCameraPos)-IN[0].vertex); 108 float3 cy=float3(0,1,0); 109 float3 right=normalize(cross(cy, dir)); 110 float3 up=normalize(cross(dir,right)); 111 float3 p=cy * radius; 112 //第一个点 113 float3 p1=float3((cos(radians(initOffsetAngle)) * (p).x)-(sin(radians(initOffsetAngle)) * (p).y),(sin(radians(initOffsetAngle)) * (p).x)+(cos(radians(initOffsetAngle)) * (p).y),(p).z); 114 float3 p2=float3(0,0,0); 115 float3 p3=float3(0,0,0); 116 float angle= radians(360/verticesNumber); 117 p2=float3((cos(angle) * (p1).x)-(sin(angle) * (p1).y),(sin(angle) * (p1).x)+(cos(angle) * (p1).y),(p1).z); 118 119 float3 p1p=(p1.x*right+p1.y*up+p1.z*dir); 120 p2=(p2.x*right+p2.y*up+p2.z*dir); 121 122 float3 edgeA = p2 - p1p; 123 float3 edgeB = p3 - p1p; 124 float3 normalFace = normalize(cross(edgeA, edgeB)); 125 o.norg=-normalFace; 126 127 ADD_INFO(p0,p1p,p2,o,tristream); 128 129 for (float i=1.0; i<verticesNumber;i+=1.0) 130 { 131 p2=float3((cos(i*angle) * (p1).x)-(sin(i*angle) * (p1).y),(sin(i*angle) * (p1).x) + (cos(i*angle) * (p1).y),(p1).z); 132 p3=float3((cos((i+1)*angle) * (p1).x)-(sin((i+1)*angle) * (p1).y),(sin((i+1) * angle) * (p1).x)+(cos((i+1) * angle) * (p1).y),(p1).z); 133 134 p2=(p2.x*right+p2.y*up+p2.z*dir); 135 p3=(p3.x*right+p3.y*up+p3.z*dir); 136 137 float3 edgeA = p2 - p1; 138 float3 edgeB = p3 - p1; 139 float3 normalFace = normalize(cross(edgeA, edgeB)); 140 o.norg=-normalFace; 141 142 ADD_INFO(p0,p2,p3,o,tristream); 143 } 144 } 145 else 146 { 147 //等像素 148 float4 pp0=float4(0,0,0,1); 149 pp0= GetScreenPos(pp0); 150 float2 up=float2(0,1); 151 float2 pp=up * radius; 152 //第一个点 153 float4 pp1=pp0; 154 float4 pp2=pp0; 155 float4 pp3=pp0; 156 pp1.xy= float2((cos(radians(initOffsetAngle)) * (pp).x)-(sin(radians(initOffsetAngle)) * (pp).y),(sin(radians(initOffsetAngle)) * (pp).x)+(cos(radians(initOffsetAngle)) * (pp).y)); 157 float angle= radians(360/verticesNumber); 158 pp2.xy=float2((cos(angle) * (pp1).x)-(sin(angle) * (pp1).y),(sin(angle) * (pp1).x)+(cos(angle) * (pp1).y)); 159 float3 edgeA = pp2 - pp1; 160 float3 edgeB = pp3 - pp1; 161 float3 normalFace = normalize(cross(edgeA, edgeB)); 162 o.norg=-normalFace; 163 164 ADD_INFO_Other(pp0,pp1+float4(pp0.xy,0,0),pp2+float4(pp0.xy,0,0),o,tristream); 165 166 for (float i=1.0; i<verticesNumber;i+=1.0) 167 { 168 pp2=pp0; 169 pp3=pp0; 170 pp2.xy=float2((cos(i*angle) * (pp1).x)-(sin(i*angle) * (pp1).y),(sin(i*angle) * (pp1).x) + (cos(i*angle) * (pp1).y)); 171 pp3.xy=float2((cos((i+1)*angle) * (pp1).x)-(sin((i+1)*angle) * (pp1).y),(sin((i+1) * angle) * (pp1).x)+(cos((i+1) * angle) * (pp1).y)); 172 173 float3 edgeA = pp2 - pp1; 174 float3 edgeB = pp3 - pp1; 175 float3 normalFace = normalize(cross(edgeA, edgeB)); 176 o.norg=-normalFace; 177 178 ADD_INFO_Other(pp0,pp2+float4(pp0.xy,0,0),pp3+float4(pp0.xy,0,0),o,tristream); 179 } 180 } 181 } 182 fixed4 frag (g2f i) : SV_Target 183 { 184 return _Color; 185 } 186 ENDCG 187 } 188 } 189 }
代码分析:
1.从模型坐标-剪裁空间坐标-屏幕空间坐标,如下:
float4 GetScreenPos(float4 vertex) { float4 pos = UnityObjectToClipPos(vertex); //return getScreenPosByClipPos(pos); pos = ComputeScreenPos(pos); pos.xy = pos.xy / pos.w; pos.xy = pos.xy * _ScreenParams.xy; return pos; }
2.屏幕坐标扩展完坐标转回剪裁空间
//将旋转完的坐标转回剪裁空间下 void ADD_VERTEX_Other(float4 v,g2f o,inout TriangleStream<g2f> tristream) { float4 pos=v; pos.xy=v.xy/_ScreenParams.xy; pos.xy=pos.xy * v.w; pos.y= (pos.y-(0.5*pos.w))/(_ProjectionParams.x * 0.5); pos.x=(pos.x-(0.5*pos.w))/0.5; o.vertex = pos; tristream.Append(o); }
这个地方就是转屏幕坐标的逆运算,主要注意的是定义位于UnityCG.cginc中的内置方法ComputeScreenPos,它的具体逻辑是这样:
inline float4 ComputeScreenPos (float4 pos)
{
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
o.zw = pos.zw;
return o;
}
即主要做的是就是这:
o.x = (pos.x * 0.5 + pos.w * 0.5)
o.y = (pos.y * 0.5 * _ProjectionParams.x + pos.w * 0.5)
这一步别忘了逆运算回去。
3.这里的算法逻辑是这样:给我一个中心点坐标,我求出正上方点坐标,根据你是几边形,去旋转(乘以旋转矩阵)得到扩展点。上面矢量点主要问题在于,我构建了本地坐标系,旋转就会有坑,因为我正上方点是通过构建坐标系去求得,然而旋转应该在自身坐标系下,
正上方点就是(0,1,0 )而不是自己构建坐标系下的up,记得转完返回去,所以这是一个坑。
方案二:
1 Shader "MyShader/PointTwo" 2 { 3 Properties 4 { 5 _Color("MianColor",color)=(1,1,1,1) 6 } 7 SubShader 8 { 9 10 Tags { "RenderType"="Opaque" } 11 LOD 100 12 Cull Off 13 14 CGINCLUDE 15 16 #include "UnityCG.cginc" 17 18 struct appdata 19 { 20 float4 vertex : POSITION; 21 float2 uv : TEXCOORD0; 22 float3 normal : NORMAL; 23 fixed4 color : COLOR; //顶点自身的颜色 24 }; 25 26 struct v2g 27 { 28 float4 vertex : POSITION; 29 float3 nor:NORMAL; 30 fixed4 color : COLOR; //顶点自身的颜色 31 }; 32 33 struct g2f 34 { 35 float4 vertex : SV_POSITION; 36 float3 pos: TEXCOORD0; 37 float3 centerPos: TEXCOORD1; 38 float3 norg:NORMAL; 39 fixed4 color : COLOR; //顶点自身的颜色 40 }; 41 42 //点的颜色 43 fixed4 _Color; 44 //边框的颜色 45 fixed4 _LineColor; 46 //点的半径大小(四边形内切圆) 47 float _Radius; 48 //点的线框宽度 49 uniform float _LineWidth=5; 50 //什么图形(-1圆/) 51 float _PointType=-1; 52 //是否是像素点 53 float IsEqualPixelPoint=0.0; 54 //交叉点宽度 55 float _CrossPointWidth; 56 57 //矢量点添加扩展点坐标转换 58 void ADD_VERTEX(float3 v,g2f o,inout TriangleStream<g2f> tristream) 59 { 60 o.vertex = UnityObjectToClipPos(v); 61 o.pos = v; 62 tristream.Append(o); 63 } 64 65 //矢量点添加扩展点面逻辑 66 void ADD_INFO(float3 p0,float3 p1,float3 p2,g2f o,inout TriangleStream<g2f> tristream) 67 { 68 ADD_VERTEX(p0,o,tristream); 69 ADD_VERTEX(p1,o,tristream); 70 ADD_VERTEX(p2,o,tristream); 71 tristream.RestartStrip(); 72 } 73 74 // (像素点)根据模型坐标获得屏幕坐标 75 float4 GetScreenPos(float4 vertex) 76 { 77 float4 pos = UnityObjectToClipPos(vertex); 78 79 pos = ComputeScreenPos(pos); 80 pos.xy = pos.xy / pos.w; 81 pos.xy = pos.xy * _ScreenParams.xy; 82 return pos; 83 } 84 85 //(像素点)将旋转完的坐标转回剪裁空间下 86 void ADD_VERT_Other(float4 v,g2f o,inout TriangleStream<g2f> tristream) 87 { 88 float4 pos=v; 89 pos.xy=v.xy/_ScreenParams.xy; 90 pos.xy=pos.xy * v.w; 91 92 pos.y= (pos.y-(0.5*pos.w))/(_ProjectionParams.x * 0.5); 93 pos.x=(pos.x-(0.5*pos.w))/0.5; 94 95 o.vertex = pos; 96 o.pos=v; 97 tristream.Append(o); 98 } 99 100 //像素点添加点 101 void ADD_INFO_Other(float4 p0,float4 p1,float4 p2,g2f o,inout TriangleStream<g2f> tristream) 102 { 103 ADD_VERTEX_Other(p0,o,tristream); 104 ADD_VERTEX_Other(p1,o,tristream); 105 ADD_VERTEX_Other(p2,o,tristream); 106 tristream.RestartStrip(); 107 } 108 109 // 判断点是否在三角形内 110 float PointinTriangle(float2 A, float2 B, float2 C, float2 P) 111 { 112 float2 ab=B-A; 113 float2 ac=C-A; 114 float2 ap=P-A; 115 116 float u=(dot(ab,ab) *dot(ap,ac)-dot(ac,ab)* dot(ap,ab)) / (dot(ac,ac) * dot(ab,ab)-dot(ac,ab) * dot(ab,ac)); 117 float v=(dot(ac,ac) *dot(ap,ab)-dot(ac,ab)* dot(ap,ac)) / (dot(ac,ac) * dot(ab,ab)-dot(ac,ab) * dot(ab,ac)); 118 119 if (u < 0 || u > 1) // if u out of range, return directly 120 { 121 return 2 ; 122 } 123 124 if (v < 0 || v > 1) // if v out of range, return directly 125 { 126 return 2 ; 127 } 128 return u+v; 129 } 130 131 //截圆点 132 void DrawCirclePoint(g2f i,float type) 133 { 134 135 //边框 136 _LineWidth=5; 137 138 float _R = abs(type) < 0.05 ? _Radius : (_Radius - _LineWidth); 139 140 float dis=distance(i.centerPos,i.pos); 141 142 //剔除圆 143 if(dis > _R) 144 { 145 discard; 146 } 147 148 } 149 150 //截正方形点 151 void DrawSquarePoint(g2f i,float type) 152 { 153 //边框 154 _LineWidth=5; 155 156 float _R = abs(type) < 0.05 ? 0.9 * _Radius : (0.9 *_Radius - _LineWidth); 157 158 //计算边缘点 159 float2 leftTop=float2(i.centerPos.x - _R , i.centerPos.y + _R); 160 float2 leftButtom=float2(i.centerPos.x - _R , i.centerPos.y - _R); 161 float2 rightTop=float2(i.centerPos.x + _R , i.centerPos.y + _R); 162 float2 rightButtom=float2(i.centerPos.x + _R , i.centerPos.y - _R); 163 164 //正方形剔除 165 if(i.pos.x < leftTop.x | i.pos.x > rightTop.x | i.pos.y < leftButtom.y | i.pos.y > rightTop.y) 166 { 167 discard; 168 } 176 } 177 178 //截三角形点 179 void DrawTrianglePoint(g2f i,float type) 180 { 181 //边框 182 _LineWidth=5; 183 184 float _R = abs(type) < 0.05 ? _Radius : (_Radius - _LineWidth); 185 186 //计算边缘点 187 float2 leftTop=float2(i.centerPos.x - _R , i.centerPos.y + _R); 188 float2 leftButtom=float2(i.centerPos.x - _R , i.centerPos.y - _R); 189 float2 rightTop=float2(i.centerPos.x + _R , i.centerPos.y + _R); 190 float2 rightButtom=float2(i.centerPos.x + _R , i.centerPos.y - _R); 191 192 //三角形顶点 193 float2 topCenter=(leftTop+rightTop)/2; 194 float2 rightB=float2((i.centerPos.x + 0.707 * _R),i.centerPos.y - 0.5 *_R); 195 float2 leftB=float2((i.centerPos.x - 0.707 * _R),i.centerPos.y - 0.5 *_R); 196 197 if(PointinTriangle(topCenter,rightB, leftB, i.pos.xy)>1) 198 { 199 discard; 200 } 259 } 260 261 //截交叉十字形点 262 void DrawCrossPoint(g2f i,float type) 263 { 264 265 //边框 266 _LineWidth=5; 267 268 _CrossPointWidth=16; 269 270 float _R = abs(type) < 0.05 ? 0.9* _Radius : (0.92 * _Radius - _LineWidth); 271 272 float _InPoint = abs(type) < 0.05 ? _CrossPointWidth / 2 : (_CrossPointWidth - _LineWidth) / 2; 273 274 //计算边缘点 275 float2 leftTop=float2(i.centerPos.x - _R , i.centerPos.y + _R); 276 float2 leftButtom=float2(i.centerPos.x - _R , i.centerPos.y - _R); 277 float2 rightTop=float2(i.centerPos.x + _R , i.centerPos.y + _R); 278 float2 rightButtom=float2(i.centerPos.x + _R , i.centerPos.y - _R); 279 280 //十字架内角点 281 float2 inRightTop=float2(i.centerPos.x + _InPoint , i.centerPos.y + _InPoint); 282 float2 inLeftTop=float2(i.centerPos.x - _InPoint , i.centerPos.y + _InPoint); 283 float2 inRightButtom=float2(i.centerPos.x + _InPoint , i.centerPos.y - _InPoint); 284 float2 inLeftButtom=float2(i.centerPos.x - _InPoint , i.centerPos.y - _InPoint); 285 286 if(((i.pos.x) > (inRightTop.x) & (i.pos.y) > (inRightTop.y)) | ((i.pos.x) < (inLeftTop.x) & (i.pos.y) > (inLeftTop.y)) | ((i.pos.x) > (inRightButtom.x) & (i.pos.y) < (inRightButtom.y)) | ((i.pos.x) < (inLeftButtom.x) & (i.pos.y) < (inLeftButtom.y)) | ((i.pos.x) < (i.centerPos.x - _R) | (i.pos.x) > (i.centerPos.x+ _R)) | ((i.pos.y) < (i.centerPos.y- _R) | (i.pos.y) > (i.centerPos.y+ _R))) 287 { 288 discard; 289 } 298 } 299 300 v2g vert(appdata_full v) 301 { 302 v2g o; 303 o.vertex = v.vertex; 304 o.nor=v.normal; 305 o.color=v.color; 306 return o; 307 } 308 309 [maxvertexcount(12)] 310 void geom(triangle v2g IN[3], inout TriangleStream<g2f> tristream) 311 { 312 g2f o; 313 //固定只扩展4个点,且初始偏转为45 314 //矢量点 315 if(IsEqualPixelPoint==0.0) 316 { 317 //中心点 318 float3 centerPos=float3(0,0,0); 319 o.centerPos=centerPos; 320 o.color=IN[0].color; 321 322 //计算本地坐标系 323 float3 dir=normalize(mul(unity_WorldToObject,_WorldSpaceCameraPos)-IN[0].vertex); 324 float3 worldUp=float3(0,1,0); 325 float3 right=normalize(cross(worldUp, dir)); 326 float3 up=normalize(cross(dir,right)); 327 328 //正上方点 329 float3 p=worldUp * _Radius * 1.414; 330 float3 p1=centerPos; 331 float3 p2=centerPos; 332 float3 p3=centerPos; 333 float3 p4=centerPos; 334 335 //计算偏移角 336 float angle= radians(360/4); 337 338 //求正方形四顶点 339 p1=float3((cos(radians(45)) * (p).x)-(sin(radians(45)) * (p).y),(sin(radians(45)) * (p).x)+(cos(radians(45)) * (p).y),(p).z); 340 p2=float3((cos(angle) * (p1).x)-(sin(angle) * (p1).y),(sin(angle) * (p1).x)+(cos(angle) * (p1).y),(p1).z); 341 p3=float3((cos(angle * 2) * (p1).x)-(sin(angle * 2) * (p1).y),(sin(angle * 2) * (p1).x)+(cos(angle * 2) * (p1).y),(p1).z); 342 p4=float3((cos(angle * 3) * (p1).x)-(sin(angle * 3) * (p1).y),(sin(angle * 3) * (p1).x)+(cos(angle * 3) * (p1).y),(p1).z); 343 p1=(p1.x * right + p1.y * up + p1.z * dir); 344 p2=(p2.x * right + p2.y * up + p2.z * dir); 345 p3=(p3.x * right + p3.y * up + p3.z * dir); 346 p4=(p4.x * right + p4.y * up + p4.z * dir); 347 348 //计算法线 349 float3 edgeA = p2 - p1; 350 float3 edgeB = p3 - p1; 351 float3 normalFace = normalize(cross(edgeA, edgeB)); 352 o.norg=-normalFace; 353 354 ADD_INFO(p1,p2,p3,o,tristream); 355 ADD_INFO(p3,p4,p1,o,tristream); 356 } 357 else 358 { 359 //等像素点 360 float4 centerPos1=float4(0,0,0,1); 361 centerPos1= GetScreenPos(centerPos1); 362 float2 up=float2(0,1); 363 float2 pp=up * _Radius * 1.414; 364 //第一个点 365 float4 pp1=centerPos1; 366 float4 pp2=centerPos1; 367 float4 pp3=centerPos1; 368 float4 pp4=centerPos1; 369 370 //计算偏移角 371 float angle= radians(360/4); 372 373 //求正方形四顶点 374 pp1.xy=float2((cos(radians(45)) * (pp).x)-(sin(radians(45)) * (pp).y),(sin(radians(45)) * (pp).x)+(cos(radians(45)) * (pp).y)); 375 pp2.xy=float2((cos(angle) * (pp1).x)-(sin(angle) * (pp1).y),(sin(angle) * (pp1).x)+(cos(angle) * (pp1).y)); 376 pp3.xy=float2((cos(2 * angle) * (pp1).x)-(sin(2 * angle) * (pp1).y),(sin(2 * angle) * (pp1).x)+(cos(2 * angle) * (pp1).y)); 377 pp4.xy=float2((cos(3 * angle) * (pp1).x)-(sin(3 * angle) * (pp1).y),(sin(3 * angle) * (pp1).x)+(cos(3 * angle) * (pp1).y)); 378 379 //计算偏移 380 pp1=pp1+float4(centerPos1.xy,0,0); 381 pp2=pp2+float4(centerPos1.xy,0,0); 382 pp3=pp3+float4(centerPos1.xy,0,0); 383 pp4=pp4+float4(centerPos1.xy,0,0); 384 385 //计算法线 386 float3 edgeA = pp2 - pp1; 387 float3 edgeB = pp3 - pp1; 388 float3 normalFace = normalize(cross(edgeA, edgeB)); 389 o.norg=-normalFace; 390 391 o.centerPos=centerPos1; 392 o.color=IN[0].color; 393 394 ADD_INFO_Other(pp1,pp2,pp3,o,tristream); 395 ADD_INFO_Other(pp3,pp4,pp1,o,tristream); 396 397 } 398 } 399 400 fixed4 frag (g2f i) : SV_Target 401 { 402 403 //圆形点 404 if (abs(_PointType)<=0.05) 405 { 406 DrawCirclePoint(i,0.0); 407 } 408 409 //正方形 410 if(abs(_PointType-2)<=0.05) 411 { 412 DrawSquarePoint(i,0.0); 413 } 414 415 //三角形 416 if(abs(_PointType-1)<=0.05) 417 { 418 DrawTrianglePoint(i,0.0); 419 } 420 421 //交叉十字 422 if(abs(_PointType-3)<=0.05) 423 { 424 DrawCrossPoint(i,0.0); 425 } 426 427 return _LineColor; 428 } 429 430 431 fixed4 fragOther (g2f i) : SV_Target 432 { 433 //圆形点 434 if (abs(_PointType)<=0.05) 435 { 436 DrawCirclePoint(i,1.0); 437 } 438 439 //正方形 440 if(abs(_PointType-2)<=0.05) 441 { 442 DrawSquarePoint(i,1.0); 443 } 444 445 //三角形 446 if(abs(_PointType-1)<=0.05) 447 { 448 DrawTrianglePoint(i,1.0); 449 } 450 451 //交叉十字 452 if(abs(_PointType-3)<=0.05) 453 { 454 DrawCrossPoint(i,1.0); 455 } 456 457 return _Color; 458 } 459 460 ENDCG 461 462 Pass 463 { 464 NAME "InitShader" 465 466 Tags { "RenderType"="Opaque" } 467 LOD 100 468 Cull Off 469 470 CGPROGRAM 471 472 #pragma target 4.0 473 #pragma vertex vert 474 #pragma geometry geom 475 #pragma fragment frag 476 477 ENDCG 478 } 479 480 Pass 481 { 482 NAME "LineShader" 483 484 Tags { "RenderType"="Opaque" } 485 LOD 100 486 Cull Off 487 488 CGPROGRAM 489 490 #pragma target 4.0 491 #pragma vertex vert 492 #pragma geometry geom 493 #pragma fragment fragOther 494 495 ENDCG 496 } 497 } 498 499 FallBack "Diffuse" 500 }
代码分析:
这个方案主要是为了边框处理,我在这里画了两次,也可以处理一次,通过条件去改变Color,但是我这里不想过多处理过渡问题,所以采取了两个Pass渲两遍。。
这里附带一个判断点是否在三角形内算法:
对于三角形ABC和一点P,可以有如下的向量表示:
p点在三角形内部的充分必要条件是:1 >= u >= 0, 1 >= v >= 0, u+v <= 1。
已知A,B,C,P四个点的坐标,可以求出u,v,把上面的式子分别点乘向量AC和向量AB
解方程得到:
解出u,v后只需要看他们是否满足“1 >= u >= 0, 1 >= v >= 0, u+v <= 1”,如满足,则,p 在三角形内。
(u = 0时,p在AB上, v = 0时,p在AC上,两者均为0时,p和A重合)
ps:是不是很熟悉,嘿嘿
关于GS:
1.属性语法在着色器定义之前设置最大顶点数:
[maxvertexcount(N)]
N是几何着色器为单个调用输出的顶点的最大数量
GS可以在每次调用时输出的顶点数量是可变的,但不能超过定义的最大值。出于性能考虑,最大顶点数应尽可能小; [NVIDIA08]指出,当GS输出在1到20个标量之间时,可以实现GS的性能峰值,如果GS输出在27-40个标量之间,则性能下降50%。
2.GS需要两个参数:输入参数和输出参数。 (实际上,它可能需要更多,但这是一个特殊的主题;请参见§11.2.4。)输入参数一般是一个定义了图元的顶点数组 — 一个顶点定义一个点,两个定义一条线,三个定义三角形,四个定义邻接线段,六个定义邻接三角形。 输入顶点的顶点类型是顶点着色器返回的顶点类型(例如,VertexOut)。 输入参数必须以原始类型为前缀,描述输入到几何着色器中的图元的类型。这可以是以下任何一种:
1、point:输入图元是点。
2、line:输入图元是线段(lists或strips)。
3、triangle:输入图元是三角形(lists或strips)。
4、lineadj:输入图元是具有邻接(lists或strips)的线段。
5、triangleadj:输入图元是具有邻接(lists或strips)的三角形。
3.几何着色器输出的顶点形成基元; 输出原语的类型由流类型(PointStream,LineStream,TriangleStream)指示。 对于线和三角形,输出原语总是strips。 然而,线和三角形列表可以使用内在的RestartStrip方法进行
关于SubShader使用:
subShaderc 中 选择执行哪个?可根据LOG设置,(从上往下 &从大到小)写)<=LOD值就执行相应的子着色器,它会在子着色器列表中找《=LOD的,但是会找第一个满足条件的,所以从小到大,会只跑第一个最小的了,所以从大到小写(LOD)