这是我正在测试我的glsl代码的场景。白线是我根据three.js中基本CylinderGeometry的缩放和转换绘制的激光束。灰球是“镜子球”。我可以在镜子球的任何方向移动,如果球和激光相交,我想做的是使激光从球反射。但是,根据我的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 {}部分,这意味着直线和球体永远不会被视为相交。如果我移动球体与线相交,结果将是这样。
如果我移动照相机以查看球的内部,则球内的激光部分仍然存在,这意味着if语句无法正确运行。
我从这里的所有https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection中得到了所有方程式,并且我仔细检查了代码是否正确实现了方程式。为什么会发生这种情况?