ওয়েবজিএল সর্বমুখী ছায়া ম্যাপিং ইস্যু


9

প্রথমত, আমি বলতে চাই যে আমি গভীরতার মানচিত্র এবং কিউব্যাপগুলি ব্যবহার করে শ্যাডো ম্যাপিং সম্পর্কে প্রচুর পোস্ট পড়েছি এবং আমি বুঝতে পারি তারা কীভাবে কাজ করে এবং এছাড়াও ওপেনজিএল ব্যবহার করে তাদের সাথে আমার কাজ করার অভিজ্ঞতা রয়েছে তবে আমার বাস্তবায়ন করার একটি সমস্যা আছে "EZ3" নামক আমার 3D গ্রাফিক্স ইঞ্জিনে একক পয়েন্ট আলোর উত্স ব্যবহার করে সর্বশক্তি ছায়া ম্যাপিং কৌশল। আমার ইঞ্জিন একটি ওয়েব থ্রিডি গ্রাফিক্স এপিআই এবং জাভাস্ক্রিপ্ট প্রোগ্রামিং ভাষা হিসাবে ব্যবহার করে, এটি কম্পিউটার বিজ্ঞানে আমার স্নাতক থিসিসের জন্য।

মূলত এইভাবেই আমি আমার ছায়া ম্যাপিং অ্যালগরিদম বাস্তবায়ন করেছি, তবে আমি কেবল পয়েন্ট লাইটের ক্ষেত্রে ফোকাস করব কারণ তাদের সাথে আমি সর্বজনীন ছায়া ম্যাপিং সংরক্ষণাগারভুক্ত করতে পারি।

প্রথমত, আমি সক্রিয়ভাবে সম্মুখ মুখটি এইভাবে কুলিং করছি:

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;
  }

দ্বিতীয়ত, প্রতিটি কিউব্যাপের মুখের জন্য গভীরতার মানগুলি রেকর্ড করার জন্য আমি একটি গভীরতা প্রোগ্রাম তৈরি করি, এটি GLSL 1.0 এ আমার গভীরতার প্রোগ্রাম কোড:

ভার্টেক্স শ্যাডার:

precision highp float;

attribute vec3 position;

uniform mat4 uModelView;
uniform mat4 uProjection;

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

খণ্ড শ্যাডার:

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);
}

তৃতীয়ত, এটি আমার জাভাস্ক্রিপ্ট ফাংশনের মূল অংশ যা "আর্কাইভগুলি" সর্বজনীন ছায়া ম্যাপিংয়ের কাজ করে

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 ...
    }
  }

চতুর্থত, আমি এইভাবে আমার প্রধান ভার্টেক্স শ্যাডার এবং টুকরা শ্যাডারে আমার গভীরতা কিউব্যাপটি ব্যবহার করে সর্বজনীন নির্দেশক ছায়া ম্যাপিংয়ের গণনা করি:

ভার্টেক্স শ্যাডার:

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);
}

খণ্ড শ্যাডার:

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;
}

অবশেষে, এটি আমি পেয়ে যাচ্ছি ফলাফলটি আমার দৃশ্যে একটি বিমান, একটি ঘনক্ষেত এবং একটি গোলক রয়েছে। তদতিরিক্ত, লাল উজ্জ্বল গোলকটি পয়েন্ট আলোর উত্স:

সার্বজনীন ছায়া ম্যাপিং ইস্যু

আপনি দেখতে পাচ্ছেন, আমি পয়েন্ট হালকা গভীরতার ফ্রেমবফারের কিউব্যাপের মতো মনে হচ্ছে এটি তাদের মুখের মধ্যে একটি ভাল দ্রবণীয়তা করছে না।

এখন অবধি, এটিকে কীভাবে সমাধান করা যায় সে সম্পর্কে আমার কোনও ধারণা নেই।


এটি একটি ভাল প্রশ্ন বলে মনে হয়েছিল - সমাধানটি খুঁজে পাওয়ার কারণে আপনি কি এটি মুছে ফেলেছিলেন? যদি তা হয় তবে আপনি এটি মুছে ফেলতে এবং আপনার সমাধান সহ একটি উত্তর পোস্ট করতে পারেন। আপনার নিজের প্রশ্নের উত্তর দেওয়া উত্সাহিত এবং আপনি প্রশ্ন এবং উত্তর উভয়ের জন্য খ্যাতি অর্জন করেন। প্লাস এটি ভবিষ্যতে একই রকম সমস্যাযুক্ত অন্য কাউকে সহায়তা করতে পারে ...
ট্রাইকোপল্যাক্স

1
হ্যালো @ থ্রিচোপ্লেক্স আসলে সমাধানটি পেয়েছি, আমি আমার নিজের প্রশ্নের উত্তর দিয়ে প্রত্যেকের সাথে উত্তরটি ভাগ করব। সত্যিই আমি আমার প্রশ্নটি মুছে ফেলেছি কারণ আমি ভেবেছিলাম যে এই সমস্যাটির জন্য কারওই যত্ন নেই।
czapata91

1
বিটিডাব্লু, শিরোনামে "সলভড" দিয়ে প্রশ্নটি সম্পাদনা করার পরিবর্তে কেবল নিজের উত্তরটি গ্রহণ করা ভাল। (এটি করার জন্য পোস্ট করার পরে সাইটটি আপনাকে একদিন অপেক্ষা করতে পারে; আমার মনে নেই))
নাথান রেড

হে! @ নাথানরিদ আমি শিরোনাম পরিবর্তন করব, সে সম্পর্কে ধন্যবাদ :)
czapata91

উত্তর:


7

সমাধান

কয়েক দিন পরে আমি বুঝতে পেরেছিলাম যে আমি আমার প্রোজেকশন ম্যাট্রিক্স ডিগ্রিতে একটি এফওভি এঙ্গেল ব্যবহার করে গণনা করছি এবং এটি রেডিয়ানে হওয়া উচিত । আমি রূপান্তর করেছি এবং এখন সবকিছু দুর্দান্ত কাজ করে। আমার গভীরতার ফ্রেমবফারের কিউব্যাপের মুখগুলির মধ্যে সংযোগটি এখন নিখুঁত। এই কারণে, প্রতিটি একক ত্রিকোণমিতিক ফাংশনের কোণটি রেডিয়েন্সে পরিচালনা করা গুরুত্বপূর্ণ।

তদুপরি, আমি বুঝতে পেরেছিলাম যে আপনি নিজের মতামতের ম্যাট্রিক্সকে প্রশ্নে এবং এইভাবে বলেছিলেন হিসাবে গণনা করতে পারেন:

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

এই পদ্ধতির মানে হল যে আপনার দৃষ্টিকোণটি পয়েন্টলাইটের কেন্দ্রে স্থাপন করা হয়েছে এবং আপনি কেবল আপনার কিউব্যাপের প্রতিটি দিকে রেন্ডার করেন তবে এই দিকগুলি কোনটি? ভাল, এই দিকনির্দেশগুলি আপনার পয়েন্টলাইটের অবস্থানের সাথে স্যুইচ ব্লকে (প্রতিটি কিউব্যাপের মুখ অনুযায়ী) প্রতিটি লক্ষ্য যুক্ত করে গণনা করা হচ্ছে

তদ্ব্যতীত, প্রজেকশন ম্যাট্রিক্সের ওয়াই-কোঅর্ডিনেটটি ফ্লিপ করা প্রয়োজন হবে না , এই ক্ষেত্রে, পয়েন্টলাইটের দৃষ্টিভঙ্গি প্রজেকশন ম্যাট্রিক্সটি এটির (1, -1, 1) ছাড়াই আপনার জিএলএসএল শেডারকে প্রেরণ করুন কারণ আমি কাজ করছি টেক্সচারে কোনও ফ্লিপড ওয়াই-কোর্ডিনেট নেই , আমি মনে করি যে আপনি যদি আপনার ফ্লিপড টেক্সচারের ওয়াই-কোর্ডিনেটের সাথে কাজ করছেন তবে আপনার পয়েন্টলাইটের প্রজেকশন ম্যাট্রিক্সের ওয়াই-কোর্ডিনেটটি সরিয়ে ফেলা উচিত , এটি সঠিকভাবে সর্বজনীন ছায়া ম্যাপিংয়ের প্রভাব রাখার জন্য।

অবশেষে, আমি এখানে সিপিইউ / জিপিইউ পক্ষের আমার সর্বশক্তি ছায়া ম্যাপিং অ্যালগরিদমের চূড়ান্ত সংস্করণটি রেখে দেব। সিপিইউতে আমি প্রতিটি কিউব্যাপের মুখের জন্য একটি সঠিক ছায়া মানচিত্র গণনা করতে আপনাকে যা করতে হবে তার প্রতিটি পদক্ষেপ ব্যাখ্যা করব। অন্যদিকে জিপিইউ-তে, আমি আমার গভীর ফাংশন শ্যাডারে আমার গভীরতা প্রোগ্রামের শীর্ষবিন্দু / ফ্রেগমেন্ট শেডার এবং সর্বজনীন ছায়া ম্যাপিং ফাংশনটি ব্যাখ্যা করব, যাতে এই কৌশলটি শিখতে পারে এমন কাউকে সহায়তা করতে বা এই অ্যালগরিদম সম্পর্কে ভবিষ্যতের সন্দেহগুলি সমাধান করতে :

সিপিইউ

  // 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);
    }
  }

রেন্ডারমেশডেপথ ফাংশনে আমি:

  // 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);

জিপিইউ

গভীরতা প্রোগ্রাম ভার্টেক্স শেডার:

precision highp float;

attribute vec3 position;

uniform mat4 uModelView;
uniform mat4 uProjection;

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

গভীরতা প্রোগ্রাম টুকরা শেডার:

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);
}

আমার প্রধান টুকরা শ্যাডারে সর্বমুখী ছায়া ম্যাপিং ফাংশন:

// 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;
}

এখানে আপনার কাছে অ্যালগরিদমের চূড়ান্ত রেন্ডার রয়েছে

এখানে চিত্র বর্ণনা লিখুন

সুন্দর গ্রাফিক্স কোডিং মজা করুন, শুভকামনা :)

সি জেড

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.