跳到主要内容
← Math

Math

Normal Space

和36是一个写法

实时渲染 · 网格 GLSL

第 19 课:Normal Space

我的解:和36是一个写法

我的解

uniform sampler2D iDepthBuffer;
uniform mat4 projectionMatrix;
// uniform mat4 viewMatrix;

const float PI = 3.14;
const float c_Error = 1e-6;

varying vec3 vPos;
varying vec3 vNormal;

mat3 getNormalSpace(vec3 normal) {
  //随便某个单位向量,找一个很可能不和normal共线的方向
  vec3 someVec = vec3(1.0, 0.0, 0.0);
  //判断两者是否接近,几乎共线接近0,否则接近1
  float dd = dot(someVec, normal);
  //作为x向量备选
  vec3 tangent = vec3(0.0, 1.0, 0.0);
  //如果足够接近,小于c_Error;
  if (1.0 - abs(dd) > c_Error) {
    //垂直于两个向量的向量,作为x向量
    tangent = normalize(cross(someVec, normal));
  }
  //z向量
  vec3 bitangent = cross(tangent, normal);
  //y肯定是法向量
  return mat3(tangent, normal, bitangent);
}

void main() {
  mat3 normalSpace = getNormalSpace(vNormal);

  float occlusion = 0.0;
  for (int i = 0; i < 4; ++i){
    for (int j = 0; j < 16; ++j){

      float theta = float(i) * (PI * 0.5)/4.0;
      float phi = float(j) * (2.0 * PI)/16.0;

      vec3 localDir = vec3(
        sin(phi) * sin(theta),
        cos(theta),
        cos(phi) * sin(theta)
      );

      vec3 worldDir = normalize(normalSpace * localDir);
      vec3 samplePos = vPos + normalize(worldDir) * 0.01;

      // world pos to screen space
      vec4 proj = projectionMatrix * viewMatrix * vec4(samplePos, 1.0);
      proj.xyz /= proj.w; // prospective division
      proj.xyz  = proj.xyz * 0.5 + 0.5; // transformation to the interval [0, 1]

      float pDepth = texture(iDepthBuffer, proj.xy).r;
      float bias = c_Error;
      float rangeCheck = smoothstep(0.0, 1.0, 0.01 / abs(proj.z - pDepth));
      float occluded = (proj.z - bias > pDepth ? 1.0 : 0.0);
      occlusion += occluded * rangeCheck;
      }
  }
  occlusion = 1.0 - occlusion / 64.0;

  gl_FragColor = vec4(vec3(occlusion) * vec3(0.8), 1.0);
}

复盘笔记

  • 和36是一个写法
  • <2025/05/29>