Почему лазер не отражается от сферы в этом коде?(GLSL, three.js) - PullRequest
0 голосов
/ 13 февраля 2019

Это сцена, на которой я тестирую свой код glsl. A laser beam and a mirror ball Белая линия - это лазерный луч, который я нарисовал на основе некоторого масштабирования и преобразования базовой CylinderGeometry в three.js.,Серый шар - это «зеркальный шар».Я могу перемещаться вокруг зеркального шара в любом направлении, и я хочу, чтобы лазер отражался от шара, если мяч и лазер пересекаются.Однако, это никогда не происходит в соответствии с моим кодом glsl.

Прежде всего, так я формирую геометрию шара и лазера.

var mirrorBallPosition = {  type: 'v3',  value: new THREE.Vector3(0.0, 3.0, -2.0)};
var laserMaterial = new THREE.ShaderMaterial({
  uniforms: {
    offset: {type: 'v3', value: new THREE.Vector3(-0.15, 2.42, -0.64)},
    mirrorBallPosition: mirrorBallPosition
    }
})
var mirrorBallMaterial = new THREE.ShaderMaterial({
  uniforms:{
    mirrorBallPosition: mirrorBallPosition
  }
});
// LOAD SHADERS
var shaderFiles = [
  'glsl/laser.vs.glsl',
  'glsl/laser.fs.glsl',
  'glsl/mirrorBall.vs.glsl',
  'glsl/mirrorBall.fs.glsl'
];
new THREE.SourceLoader().load(shaderFiles, function(shaders) {
  laserMaterial.vertexShader = shaders['glsl/laser.vs.glsl'];
  laserMaterial.fragmentShader = shaders['glsl/laser.fs.glsl'];
  mirrorBallMaterial.vertexShader = shaders['glsl/mirrorBall.vs.glsl'];
  mirrorBallMaterial.fragmentShader = shaders['glsl/mirrorBall.fs.glsl'];
})
laser = {}
//Laser geometry
laser.laserGeometry = new THREE.CylinderGeometry(0.02, 0.02, 1, 16);
for (let i = 0; i < laser.laserGeometry.vertices.length; ++i)
  laser.laserGeometry.vertices[i].y += 0.5;
laser.leftLaser = new THREE.Mesh(laser.laserGeometry, leftLaserMaterial);
laser.rightLaser = new THREE.Mesh(laser.laserGeometry, rightLaserMaterial);
scenes[Part.LASERS].add(laser.leftLaser);
scenes[Part.LASERS].add(laser.rightLaser);

laser.mirrorBallGeometry = new THREE.SphereGeometry(1,32,32);
laser.mirrorBall = new THREE.Mesh(laser.mirrorBallGeometry, mirrorBallMaterial);
scenes[Part.LASERS].add(laser.mirrorBall);
...
// SETUP UPDATE CALL-BACK
function update() {
  checkKeyboard();
  requestAnimationFrame(update);
  renderer.render(scenes[Part.LASERS], cameras[Part.LASERS]);
}
update();

И это вершинный шейдеркод для моего лазера.

#version 300 es
uniform vec3 offset;
uniform vec3 mirrorBallPosition;
void main(){
  vec3 targetPosition = vec3(0.0, 0.3, -5.0);
  vec3 laserStart = offset + vec3(0.0,1.0,0.0);
  laserStart.y = offset.y;
  mat4 T = mat4(1.0);
  T[3].xyz = laserStart;
  mat4 S = mat4(1.0);
  S[1][1] = length(laserStart-targetPosition);

  // Roll the laser horizontally
  mat4 Rx = mat4(1.0);
  Rx[1][1] = 0.0;
  Rx[1][2] = -1.0;
  Rx[2][1] = 1.0;
  Rx[2][2] = 0.0;

  vec3 laserDirection = targetPosition - laserStart;
  vec3 laserDirectionZX = laserDirection;
  laserDirectionZX.y = 0.0;
  laserDirection = normalize(laserDirection);
  laserDirectionZX = normalize(laserDirectionZX);

  mat4 Rz = mat4(1.0);
  vec3 minusZ = vec3(0.0,0.0,-1.0);
  float cosBeta = dot(minusZ, laserDirectionZX);
  float sinBeta = sqrt(1.0-(cosBeta * cosBeta));
  float flag = 1.0;
  float relativeXCoordinate = laserStart.x-targetPosition.x;
  if(relativeXCoordinate < 0.0){
    flag = -1.0;
  }
  Rz[0][0] = cosBeta;
  Rz[1][0] = -sinBeta * flag;
  Rz[0][1] = sinBeta * flag;
  Rz[1][1] = cosBeta;

  // Roll the laser vertically
  mat4 Rx_2 = mat4(1.0);
  float cosAlpha = dot(laserDirection, laserDirectionZX);
  float sinAlpha = sqrt(1.0-(cosAlpha * cosAlpha));
  Rx_2[1][1] = cosAlpha;
  Rx_2[2][1] = sinAlpha;
  Rx_2[1][2] = -sinAlpha;
  Rx_2[2][2] = cosAlpha;

    //Now this is the reflection part.
    //Got all of these equations here(URL below)
    //https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
  float r = 0.3;  //sphere size
  float temp = dot(laserDirection,laserStart-mirrorBallPosition);
  float temp2 = length(laserStart-mirrorBallPosition);
  float insideSQRT = (temp * temp) - (temp2*temp2-r*r);
  // I calculate the world position with this transformation.
  vec3 fragmentWorldPos = vec3(T * Rx * Rz * Rx_2 * S * vec4(position, 1.0));

  if(length(fragmentWorldPos-mirrorBallPosition)<r){    //This is when the laser should reflect.
    float d = -temp - sqrt(insideSQRT);
    vec3 closestIntersect = (laserStart+(d*laserDirection));
    vec3 fromCenterToIntersect = closestIntersect - mirrorBallPosition;
    fromCenterToIntersect = normalize(fromCenterToIntersect);
    vec3 lightComingIn = closestIntersect-laserStart;
    vec3 reflectDir = reflect(lightComingIn, fromCenterToIntersect);
    reflectDir = normalize(reflectDir);
    float distance = length(fragmentWorldPos-closestIntersect);
    vec3 updatedWorldPosition = closestIntersect + (reflectDir * distance);
    gl_Position = projectionMatrix * viewMatrix * vec4(updatedWorldPosition,1.0);
  }
  else{ // This is when the laser should not reflect.
    gl_Position = projectionMatrix * viewMatrix * vec4(fragmentWorldPos, 1.0);
  }

}

Это довольно длинный код, но единственной важной частью должна быть инструкция if-else в последней части.

  float r = 0.3;  //sphere size
  float temp = dot(laserDirection,laserStart-mirrorBallPosition);
  float temp2 = length(laserStart-mirrorBallPosition);
  float insideSQRT = (temp * temp) - (temp2*temp2-r*r);
  vec3 fragmentWorldPos = vec3(T * Rx * Rz * Rx_2 * S * vec4(position, 1.0));

  if(length(fragmentWorldPos-mirrorBallPosition)<r){    //This is when the laser should reflect.
    float d = -temp - sqrt(insideSQRT);
    vec3 closestIntersect = (laserStart+(d*laserDirection));
    vec3 fromCenterToIntersect = closestIntersect - mirrorBallPosition;
    fromCenterToIntersect = normalize(fromCenterToIntersect);
    vec3 lightComingIn = closestIntersect-laserStart;
    vec3 reflectDir = reflect(lightComingIn, fromCenterToIntersect);
    reflectDir = normalize(reflectDir);
    float distance = length(fragmentWorldPos-closestIntersect);
    vec3 updatedWorldPosition = closestIntersect + (reflectDir * distance);
    gl_Position = projectionMatrix * viewMatrix * vec4(updatedWorldPosition,1.0);
  }
  else{ // This is when the laser should not reflect.
    gl_Position = projectionMatrix * viewMatrix * vec4(fragmentWorldPos, 1.0);
  }

Кажется, что часть if {} никогда не выполняется, а выполняется только часть else {}, что означает, что линия и сфера никогда не считаются пересекающимися.Если я переместлю сферу, чтобы пересечь линию, результат будет следующим:

When the sphere and the line intersects

Доля лазера внутри сферы все еще там, еслиЯ перемещаю камеру, чтобы посмотреть внутрь сферы, что означает, что оператор if работает неправильно.

Я получил все свои уравнения отсюда "https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection",, и я дважды проверил свой кодесли они правильно реализовали уравнения. Есть ли причина, по которой это происходит?

...