我正在开发 GLSL 着色器,并在使用 while 循环遍历像素时遇到问题。相同的代码在桌面设备上运行良好,但在移动设备(Android 和 iOS)上产生不同的结果。
相同的逻辑在桌面和移动设备上产生不一致的效果。
我尝试在纹理区域内添加效果,但似乎会产生不同的结果。
(function() {
"use strict";
var scene;
var renderer;
var camera;
var uniforms;
var material;
const screenSize = {
desk: {
w: 640,
h: 480
},
mob: {
w: 240,
h: 320
},
};
async function loadTexture(url) {
return new Promise((resolve, reject) => {
const loader = new THREE.TextureLoader();
loader.load(
url,
(texture) => resolve(texture), // Resolve with the loaded texture
undefined, // Progress callback, if needed
(error) => reject(error) // Reject on error
);
});
}
async function init() {
var canvas = document.getElementById("three-canvas");
// Initialize WebGL Renderer
renderer = new THREE.WebGLRenderer({
canvas
});
// renderer.setSize(window.innerWidth, window.innerHeight);
const size = screen.width < 600 ? screenSize.mob : screenSize.desk;
renderer.setSize(size.w, size.h);
// Initialize Scenes
scene = new THREE.Scene();
// Initialize Camera
camera = new THREE.PerspectiveCamera(
45,
// window.innerWidth / window.innerHeight,
size.w / size.h,
1,
100
);
camera.position.z = 10;
// Create Light
var light = new THREE.PointLight(0xffffff);
light.position.set(0, 0, 500);
scene.add(light);
// Create Ball
var vertShader = document.getElementById("vertexShader").innerHTML;
var fragShader = document.getElementById("fragmentShader").innerHTML;
const geometry = new THREE.PlaneGeometry(8, 8);
const texture = await loadTexture("https://camweara-customers.s3.ap-south-1.amazonaws.com/Teststore/test.png");
console.log("texture: ", texture);
uniforms = {
texture1: {
type: "t",
value: texture
},
// mouse: { type: 'v2', value: new THREE.Vector2(0.0, 0.0) }
};
console.log("uniforms: ", uniforms);
// Create a shader material
material = new THREE.ShaderMaterial({
vertexShader: vertShader,
fragmentShader: fragShader,
uniforms: uniforms,
transparent: true,
});
// Create the plane mesh
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
renderer.render(scene, camera);
}
window.onload = function() {
init();
animate();
};
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js">
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100%;
overflow: hidden;
background: #1d1d1d;
}
canvas {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<canvas id="three-canvas"></canvas>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision highp float;
uniform sampler2D texture1;
varying vec2 vUv;
void main() {
vec4 bg_clr = texture2D(texture1, vUv);
vec4 finalClr = vec4(0.0, 0.0, 1.0, 1.0);
float pixelStep = 0.001;
vec4 finalX;
vec4 finalY;
if(bg_clr.w > 0.2){
float i = 0.0;
while(i < 1.0){
vec2 pos = vUv - vec2(i, 0.0);
vec4 xColor = texture2D(texture1, pos);
if(xColor.w < 0.01){
finalX = vec4(1.0,0.0,0.0,1.0);
break;
}
i = i + pixelStep;
}
i=0.0;
while(i < 1.0){
vec2 pos = vUv + vec2(0.0, i);
vec4 xColor = texture2D(texture1, pos);
if(xColor.w < 0.1){
finalY = vec4(0.0,1.0,0.0,1.0);
break;
}
i = i + pixelStep;
}
finalClr = mix(finalX,finalY,0.5);
}
gl_FragColor = finalClr;
}
</script>
</body>
</html>
我不确定是什么导致了您的问题,但我有一些想法,希望其中一些能对您有所帮助。
首先,移动驱动程序被认为不可靠,有时不遵循规范。
如果您的硬件、浏览器或驱动程序较旧,则可能不支持 WebGL 2,并且 Three.js 会回退到 WebGL 1。在 WebGL 1 中,片段着色器中不强制支持
highp
。因此,当您声明 precision highp float;
时,您可能会导致未定义的行为。您可以在 renderer.capabilities.isWebGL2
和 renderer.capabilities.precision
中检查这些内容。
除此之外,我没有看到任何未定义的行为。您在 (0-1) 区间之外对纹理进行采样,这应该不是问题,但它是移动的,所以谁知道呢?
但是,我可以看到你的代码很不寻常。您可以在着色器中使用
while
、if
和 break
跳转到代码的其他部分。由于在不同的片段着色器调用中,循环在不同的时间退出,这将导致着色器发散,迫使 GPU 或多或少以串行方式执行代码,而不是使用其许多内核,这违背了 GPU 的目的。此外,您可能会在一次片段着色器调用中对纹理进行 2000 次采样。您可以在一个帧中调用数百万个片段着色器。这是大量的纹理采样,这是对性能要求最高的操作之一。这可能会非常慢。对于较小的纹理,缓存可能会节省你的时间,但对于较大的纹理,它会非常慢。
我不确定你想要达到的期望结果是什么,但是如果你想用一种颜色对低于某个 alpha 阈值的像素进行着色,并用另一种颜色对上面的像素进行着色,那么它可以大大简化:
void main() {
vec4 bg_clr = texture2D(texture1, vUv);
gl_FragColor = mix(vec4(0.0, 0.0, 1.0, 1.0), vec4(0.5,0.5,0.0,1.0), bvec4(bg_clr.w > 0.2));
}