Проблема вызвана тем, что (из OpenGL ES Shading Language 1.00 Спецификация - 4.3.5 Варьирование )
переменные переменные установлены для каждой вершины и интерполируются в перспективе -правильно по отношению к визуализируемому примитиву.
Чтобы ваш алгоритм работал, вы должны интерполировать направление луча noperspective
.
Поскольку GLSL ES 1.00 не обеспечивает Для квалификаторов интерполяции необходимо найти обходной путь.
Вычислить луч в фрагментном шейдере:
void main() {
bool hit = raymarchHit(worldPosition, normalize(worldPosition - cameraPosition));
gl_FragColor = hit ? vec4(1.0,0.0,0.0,1.0) : vec4(0.0,0.0,1.0,0.5);
}
См. Пример:
var camera, scene, renderer, mesh, material, stats;
class VolumetricNebula_sp {
constructor(vertexShader, fragmentShader) {
this.clock = new THREE.Clock();
this.uniforms = {
time: {
type: 'float',
value: 2.0
}
}
this.geometry = new THREE.BoxBufferGeometry(4.0, 4.0, 4.0); // width, height, depth
this.material = new THREE.ShaderMaterial({
uniforms: this.uniforms,
fragmentShader: fragmentShader,
vertexShader: vertexShader,
transparent: true
})
this.mesh = new THREE.Mesh(this.geometry, this.material);
scene.add(this.mesh);
}
update() {
this.uniforms.time.value = this.clock.getElapsedTime();
this.mesh.rotation.x += 0.01;
this.mesh.rotation.y += 0.01;
this.mesh.rotation.z += 0.01;
}
}
function init() {
// Renderer.
renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Add renderer to page
document.body.appendChild(renderer.domElement);
// Create camera.
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 5;
// Create scene.
scene = new THREE.Scene();
volVertexShader = `varying highp vec3 worldPosition;
//varying vec3 viewDirection;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
worldPosition = vec3(modelMatrix * vec4(position, 1.0));
//viewDirection = normalize(worldPosition - cameraPosition);
}`;
volFragShader = `uniform float time;
varying vec3 worldPosition;
//varying vec3 viewDirection;
/// utilities
bool sphereHit (vec3 p)
{
return distance(p,vec3(0,0,0)) < 1.0;
}
#define STEP_SIZE 0.1
bool raymarchHit (vec3 in_position, vec3 direction)
{
for (int i = 0; i < 100; i++)
{
if ( sphereHit(in_position) )
return true;
in_position += direction * STEP_SIZE;
}
return false;
}
void main() {
bool hit = raymarchHit(worldPosition, normalize(worldPosition - cameraPosition));
gl_FragColor = hit ? vec4(1.0,0.0,0.0,1.0) : vec4(0.0,0.0,1.0,0.5);
}`;
var volumetricNebulaCenterPiece = new VolumetricNebula_sp(volVertexShader, volFragShader);
// Add listener for window resize.
window.addEventListener('resize', onWindowResize, false);
// Add stats to page.
//stats = new Stats();
//document.body.appendChild( stats.dom );
function animate() {
volumetricNebulaCenterPiece.update();
renderer.render(scene, camera);
//stats.update();
requestAnimationFrame(animate);
}
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
init();
body { padding: 0; margin: 0; }
canvas { display: block; }
<!--script src="https://threejs.org/build/three.min.js"></script-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>