描边shader(法线外拓)
时间: 2023-09-15 10:24:19 浏览: 63
描边 Shader 一般是通过将物体的轮廓线提取出来,然后在轮廓线周围绘制一层边缘线,从而实现描边的效果。对于法线外拓的描边 Shader,一般是通过向模型表面法线方向推进一定的距离,然后在推进后的表面上绘制边缘线。
下面是一个简单的法线外拓描边 Shader 的实现示例:
首先,在 Vertex Shader 中需要计算出顶点在表面上被推进后的位置。这里可以通过将顶点沿着法线方向推进一定的距离来实现。具体的计算方法如下:
```glsl
varying vec3 vNormal;
void main() {
// 计算法线
vNormal = normalize(normalMatrix * normal);
// 将顶点沿法线方向推进一定距离
float offset = 0.1;
vec3 newPosition = position + vNormal * offset;
// 转换为世界坐标系
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
```
注意这里的 `normalMatrix` 是将世界坐标系中的法线转换到了视图坐标系中,这样才能保证法线方向是正确的。
然后,在 Fragment Shader 中需要根据深度和法线信息来确定哪些像素是边缘像素。这里可以通过计算相邻像素深度差和法线差来判断当前像素是否处于轮廓线上。具体的计算方法如下:
```glsl
uniform float edgeWidth;
varying vec3 vNormal;
void main() {
// 计算相邻像素深度差和法线差
float depth1 = texture2D(depthTexture, gl_FragCoord.xy / screenSize).r;
float depth2 = texture2D(depthTexture, gl_FragCoord.xy + vec2(1.0, 0.0) / screenSize).r;
float depthDiff = abs(depth2 - depth1);
vec3 normal1 = vNormal;
vec3 normal2 = normalize(texture2D(normalTexture, gl_FragCoord.xy + vec2(1.0, 0.0) / screenSize).xyz * 2.0 - 1.0);
float normalDiff = dot(normal1, normal2);
// 如果深度差和法线差都超过了一定的阈值,则当前像素为边缘像素
if (depthDiff > edgeWidth && normalDiff < 0.5) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
} else {
discard;
}
}
```
注意这里的 `depthTexture` 和 `normalTexture` 分别是渲染到纹理的深度和法线信息,需要在使用 Shader 前先渲染一遍到纹理中。
最后,将推进后的表面和描边合并即可得到最终的法线外拓描边效果。