Vấn đề lập bản đồ bóng đa hướng WebGL


9

Trước hết, tôi muốn nói rằng tôi đã đọc rất nhiều bài viết về ánh xạ bóng bằng cách sử dụng bản đồ độ sâu và hình khối và tôi hiểu cách chúng hoạt động và tôi cũng có kinh nghiệm làm việc với chúng khi sử dụng OpenGL, nhưng, tôi gặp vấn đề khi thực hiện Kỹ thuật lập bản đồ bóng đa hướng sử dụng nguồn sáng một điểm trong công cụ đồ họa 3D của tôi có tên là "EZ3". Công cụ của tôi sử dụng WebGL làm API đồ họa 3D và JavaScript làm ngôn ngữ lập trình, đây là luận án cử nhân Khoa học Máy tính của tôi.

Về cơ bản đây là cách tôi đã triển khai thuật toán ánh xạ bóng của mình, nhưng tôi sẽ chỉ tập trung vào trường hợp đèn điểm vì với chúng tôi có thể lưu trữ ánh xạ bóng đa hướng.

Đầu tiên, tôi chủ động loại bỏ mặt trước như thế này:

if (this.state.faceCulling !== Material.FRONT) {
    if (this.state.faceCulling === Material.NONE)
      gl.enable(gl.CULL_FACE);

    gl.cullFace(gl.FRONT);
    this.state.faceCulling = Material.FRONT;
  }

Thứ hai, tôi tạo một chương trình độ sâu để ghi lại các giá trị độ sâu cho từng mặt sơ đồ khối, đây là mã chương trình độ sâu của tôi trong GLSL 1.0:

Vertex Shader:

precision highp float;

attribute vec3 position;

uniform mat4 uModelView;
uniform mat4 uProjection;

void main() {
  gl_Position = uProjection * uModelView * vec4(position, 1.0);
}

Mảnh vỡ mảnh:

precision highp float;

vec4 packDepth(const in float depth) {
  const vec4 bitShift = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
  const vec4 bitMask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
  vec4 res = mod(depth * bitShift * vec4(255), vec4(256)) / vec4(255);
  res -= res.xxyz * bitMask;
  return res;
}

void main() {
  gl_FragData[0] = packDepth(gl_FragCoord.z);
}

Thứ ba, đây là phần thân của hàm JavaScript của tôi "lưu trữ" ánh xạ bóng đa hướng

program.bind(gl);

  for (i = 0; i < lights.length; i++) {
    light = lights[i];

    // Updates pointlight's projection matrix

    light.updateProjection();

    // Binds point light's depth framebuffer

    light.depthFramebuffer.bind(gl);

    // Updates point light's framebuffer in order to create it 
    // or if it's resolution changes, it'll be created again.

    light.depthFramebuffer.update(gl);

    // Sets viewport dimensions with depth framebuffer's dimensions

    this.viewport(new Vector2(), light.depthFramebuffer.size);

    if (light instanceof PointLight) {

      up = new Vector3();
      view = new Matrix4();
      origin = new Vector3();
      target = new Vector3();

      for (j = 0; j < 6; j++) {

    // Check in which cubemap's face we are ...

        switch (j) {
          case Cubemap.POSITIVE_X:
            target.set(1, 0, 0);
            up.set(0, -1, 0);
            break;
          case Cubemap.NEGATIVE_X:
            target.set(-1, 0, 0);
            up.set(0, -1, 0);
            break;
          case Cubemap.POSITIVE_Y:
            target.set(0, 1, 0);
            up.set(0, 0, 1);
            break;
          case Cubemap.NEGATIVE_Y:
            target.set(0, -1, 0);
            up.set(0, 0, -1);
            break;
          case Cubemap.POSITIVE_Z:
            target.set(0, 0, 1);
            up.set(0, -1, 0);
            break;
          case Cubemap.NEGATIVE_Z:
            target.set(0, 0, -1);
            up.set(0, -1, 0);
            break;
        }

    // Creates a view matrix using target and up vectors according to each face of pointlight's
    // cubemap. Furthermore, I translate it in minus light position in order to place
    // the point light in the world's origin and render each cubemap's face at this 
    // point of view

        view.lookAt(origin, target, up);
        view.mul(new EZ3.Matrix4().translate(light.position.clone().negate()));

    // Flips the Y-coordinate of each cubemap face
    // scaling the projection matrix by (1, -1, 1).

    // This is a perspective projection matrix which has:
    // 90 degress of FOV.
    // 1.0 of aspect ratio.
    // Near clipping plane at 0.01.
    // Far clipping plane at 2000.0.

        projection = light.projection.clone();
        projection.scale(new EZ3.Vector3(1, -1, 1));

    // Attaches a cubemap face to current framebuffer in order to record depth values for the face with this line
    // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + j, id, 0);

        light.depthFramebuffer.texture.attach(gl, j);

    // Clears current framebuffer's color with these lines:
    // gl.clearColor(1.0,1.0,1.0,1.0);
    // gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        this.clear(color);

    // Renders shadow caster meshes using the depth program

        for (k = 0; k < shadowCasters.length; k++)
          this._renderShadowCaster(shadowCasters[k], program, view, projection);
      }
    } else {
       // Directional light & Spotlight case ...
    }
  }

Thứ tư, đây là cách tôi tính toán Bản đồ bóng tối đa hướng bằng cách sử dụng sơ đồ khối sâu của mình trong Vertex Shader & Fragment Shader chính của tôi:

Vertex Shader:

precision highp float;

attribute vec3 position;

uniform mat4 uModel;
uniform mat4 uModelView;
uniform mat4 uProjection;

varying vec3 vPosition;

void main() {
  vPosition = vec3(uModel * vec4(position, 1.0));

  gl_Position = uProjection * uModelView * vec4(position, 1.0);
}

Mảnh vỡ mảnh:

float unpackDepth(in vec4 color) {
    return dot(color, vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ));
}

float pointShadow(const in PointLight light, const in samplerCube shadowSampler) {
    vec3 direction = vPosition - light.position;
    float vertexDepth = clamp(length(direction), 0.0, 1.0);
    float shadowMapDepth = unpackDepth(textureCube(shadowSampler, direction));

    return (vertexDepth > shadowMapDepth) ? light.shadowDarkness : 1.0;
}

Cuối cùng, đây là kết quả mà tôi nhận được, cảnh của tôi có một mặt phẳng, một khối lập phương và một khối cầu. Bên cạnh đó, quả cầu sáng màu đỏ là nguồn sáng điểm:

Vấn đề lập bản đồ bóng đa hướng

Như bạn có thể thấy, tôi có vẻ như sơ đồ khối của framebuffer độ sâu ánh sáng điểm nó không thực hiện một phép nội suy tốt giữa các khuôn mặt của chúng.

Cho đến bây giờ, tôi không biết làm thế nào để giải quyết điều này.


Đây có vẻ là một câu hỏi hay - bạn đã xóa nó vì bạn đã tìm ra giải pháp? Nếu vậy bạn có thể phục hồi nó và gửi câu trả lời với giải pháp của bạn. Trả lời câu hỏi của riêng bạn được khuyến khích và bạn có được danh tiếng cho cả câu hỏi và câu trả lời. Thêm vào đó, nó có thể giúp người khác gặp vấn đề tương tự trong tương lai ...
trichoplax

1
Xin chào @trichoplax thực sự tôi đã tìm thấy giải pháp, tôi sẽ chia sẻ câu trả lời với mọi người trả lời câu hỏi của riêng tôi. Thành thật tôi đã xóa câu hỏi của tôi vì tôi nghĩ không ai quan tâm đến vấn đề này.
czapata91

1
BTW, thay vì chỉnh sửa câu hỏi với "GIẢI QUYẾT" trong tiêu đề, tốt hơn là chỉ chấp nhận câu trả lời của riêng bạn. (Trang web có thể khiến bạn phải đợi một ngày sau khi đăng bài để làm điều đó; tôi không nhớ.)
Nathan Reed

Chào! @NathanReed Tôi sẽ thay đổi tiêu đề, cảm ơn về điều đó :)
czapata91

Câu trả lời:


7

GIẢI PHÁP

Sau một vài ngày, tôi nhận ra rằng tôi đang tính toán ma trận chiếu của mình bằng góc FOV theo độ và Nó sẽ được tính bằng radian . Tôi đã thực hiện chuyển đổi và bây giờ mọi thứ hoạt động tuyệt vời. Sự nội suy giữa các khuôn mặt của sơ đồ khối framebuffer sâu của tôi bây giờ là hoàn hảo. Vì lý do này, điều quan trọng là phải xử lý mọi góc của hàm lượng giác bằng radian.

Hơn nữa, tôi nhận ra rằng bạn có thể tính toán ma trận xem của mình như tôi đã nói trong câu hỏi và theo cách này:

view.lookAt(position, target.add(position.clone()), up);

Cách tiếp cận này có nghĩa là quan điểm của bạn được đặt ở trung tâm của điểm sáng và bạn chỉ hiển thị theo từng hướng của sơ đồ khối, nhưng đó là những hướng nào? tốt, các hướng này được tính toán thêm từng mục tiêu mà tôi đã ở trong khối chuyển đổi (theo khuôn mặt của từng sơ đồ khối) với vị trí đèn chiếu của bạn .

Hơn nữa, không cần thiết phải lật Tọa độ Y của ma trận chiếu , Trong trường hợp này, bạn có thể gửi ma trận chiếu phối cảnh của đèn chiếu tới trình đổ bóng GLSL của bạn mà không mở rộng theo (1, -1, 1) vì tôi đang làm việc với Hoạ tiết không có Tọa độ Y bị lật , tôi nghĩ bạn chỉ nên lật Tọa độ Y của ma trận chiếu của điểm sáng của mình nếu bạn đang làm việc với Phối hợp Y của một kết cấu bị lật , điều này để có hiệu ứng ánh xạ bóng đa hướng chính xác.

Cuối cùng, tôi sẽ rời khỏi đây phiên bản cuối cùng của thuật toán Ánh xạ bóng đa hướng của tôi về phía CPU / GPU. Về phía CPU, tôi sẽ giải thích từng bước bạn phải làm để tính toán bản đồ bóng chính xác cho từng khuôn mặt của sơ đồ khối. Mặt khác, về phía GPU, tôi sẽ giải thích chức năng tạo bóng đỉnh / mảnh và chương trình bóng tối đa hướng của chương trình độ sâu của tôi trong trình tạo bóng mảnh chính của tôi, để giúp ai đó có thể học kỹ thuật này hoặc giải quyết những nghi ngờ trong tương lai về thuật toán này :

CPU

  // Disable blending and enable front face culling.

  this.state.disable(gl.BLEND);

  this.state.enable(gl.CULL_FACE);
  this.state.cullFace(gl.FRONT);

  // Binds depth program

  program.bind(gl);

  // For each pointlight source do

  for (i = 0; i < lights.length; i++) {
    light = lights[i];

    // Get each pointlight's world position

    position = light.worldPosition();

    // Binds pointlight's depth framebuffer. Besides, in this function,
    // viewport's dimensions are set according to depth framebuffer's dimension.

    light.depthFramebuffer.bind(gl, this.state);

    // Updates point light's framebuffer in order to create it 
    // or if it's resolution have changed, it'll be created again.

    light.depthFramebuffer.update(gl);

    // Check in which cubemap's face we are ...

    for (j = 0; j < 6; j++) {
      switch (j) {
        case Cubemap.POSITIVE_X:
          target.set(1, 0, 0);
          up.set(0, -1, 0);
          break;
        case Cubemap.NEGATIVE_X:
          target.set(-1, 0, 0);
          up.set(0, -1, 0);
          break;
        case Cubemap.POSITIVE_Y:
          target.set(0, 1, 0);
          up.set(0, 0, 1);
          break;
        case Cubemap.NEGATIVE_Y:
          target.set(0, -1, 0);
          up.set(0, 0, -1);
          break;
        case Cubemap.POSITIVE_Z:
          target.set(0, 0, 1);
          up.set(0, -1, 0);
          break;
        case Cubemap.NEGATIVE_Z:
          target.set(0, 0, -1);
          up.set(0, -1, 0);
          break;
      }

      // Creates a view matrix using target and up vectors 
      // according to each face of pointlight's cubemap.

      view.lookAt(position, target.add(position.clone()), up);

      // Attaches cubemap's face to current framebuffer 
      // in order to record depth values in that direction.

      light.depthFramebuffer.texture.attach(gl, j);

      // Clears color & depth buffers of your current framebuffer

      this.clear();

      // Render each shadow caster mesh using your depth program

      for (k = 0; k < meshes.length; k++)
        this._renderMeshDepth(program, meshes[k], view, light.projection);
    }
  }

Trên hàm renderMeshDepth, tôi:

  // Computes pointlight's model-view matrix 

  modelView.mul(view, mesh.world);

  // Dispatch each matrix to the GLSL depth program

  program.loadUniformMatrix(gl, 'uModelView', modelView);
  program.loadUniformMatrix(gl, 'uProjection', projection);

  // Renders a mesh using vertex buffer objects (VBO)

  mesh.render(gl, program.attributes, this.state, this.extensions);

GPU

Chương trình độ sâu Vertex Shader:

precision highp float;

attribute vec3 position;

uniform mat4 uModelView;
uniform mat4 uProjection;

void main() {
  gl_Position = uProjection * uModelView * vec4(position, 1.0);
}

Chương trình độ sâu mảnh Shader:

precision highp float;

// The pack function distributes fragment's depth precision storing 
// it throughout (R,G,B,A) color channels and not just R color channel 
// as usual in shadow mapping algorithms. This is because I'm working
// with 8-bit textures and one color channel hasn't enough precision 
// to store a depth value.

vec4 pack(const in float depth) {
  const vec4 bitShift = vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0);
  const vec4 bitMask = vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);

  vec4 res = fract(depth * bitShift);
  res -= res.xxyz * bitMask;

  return res;
}

void main() {
  // Packs normalized fragment's Z-Coordinate which is in [0,1] interval.

  gl_FragColor = pack(gl_FragCoord.z);
}

Chức năng Ánh xạ bóng đa hướng trong trình tạo bóng mảnh chính của tôi:

// Unpacks fragment's Z-Coordinate which was packed 
// on the depth program's fragment shader.

float unpack(in vec4 color) {
   const vec4 bitShift = vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0);
   return dot(color, bitShift);
}

// Computes Omnidirectional Shadow Mapping technique using a samplerCube
// vec3 lightPosition is your pointlight's position in world coordinates.
// vec3 vPosition is your vertex's position in world coordinates, in code
// I mean this -> vPosition = vec3(uModel * vec4(position, 1.0));
// where uModel is your World/Model matrix.

float omnidirectionalShadow(in vec3 lightPosition, in float bias, in float darkness, in samplerCube sampler) {
    vec3 direction = vPosition - lightPosition;
    float vertexDepth = clamp(length(direction), 0.0, 1.0);
    float shadowMapDepth = unpack(textureCube(sampler, direction)) + bias;

    return (vertexDepth > shadowMapDepth) ? darkness : 1.0;
}

Ở đây bạn có một kết xuất cuối cùng của thuật toán

nhập mô tả hình ảnh ở đây

Vui chơi mã hóa đồ họa đẹp, chúc may mắn :)

Cz

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.