three.js - 在 Threejs 中使用 ShaderMaterial 的平面闪烁
问题描述
我正在尝试通过使用 RepeatWrapping 在一个非常大的平面(1000x1000,也缩放 10 倍)上应用纹理。当我使用 MeshBasicMaterial 时它看起来不错,但当我使用 ShaderMaterial 时它会闪烁。以下是我的代码。
<!DOCTYPE html>
<html>
<head>
<title>MeshShaderMaterialExample</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/TrackballControls.js"></script>
<script src="js/TWEEN.js"></script>
<style type="text/css">
body {
width: 100%;
height: 100%;
background-color: #000;
color: #fff;
margin: 0px;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script>
var camera, scene, renderer;
var container, mesh, geometry;
var controls, effect;
var tweenUpdate="false";
var tweenOver="true";
var textureData=
{
"texture_0":
{
"img":"gman.png"
},
"texture_1":
{
"img":"gman.png"
}}
var magicPosition = { magicTrans:0 };
var magicTarget = { magicTrans:1 };
var magicTween = new TWEEN.Tween(magicPosition).to(magicTarget, 1000);
magicTween.easing(TWEEN.Easing.Linear.None);
var currentTexture=0;
var nextTexture=0;
var uniforms = {
textures: {
value: []
},
repeat: {
type: 'f',
value: 100
},
transition: {
value: 0
},
currentUniform: {
value: 0
},
nextUniform: {
value: 0
}
};
var textureLoader = new THREE.TextureLoader();
var pics=[];
for (var i = 0; i < Object.keys(textureData).length; i++) {
var ass="texture_"+i;
pics[i]= textureData[ass].img;
console.log(pics[i]);
}
pics.forEach((p, idx)=>{
textureLoader.load(p, function(tex){
tex.needsUpdate = true;
uniforms.textures.value[idx] = tex;
uniforms.textures.value[idx].needsUpdate = true;
// console.log(tex);
uniforms.textures.value[idx].minFilter = THREE.LinearFilter;
})
});
var vertShader = `
varying vec2 vUv;
uniform float repeat;
void main()
{
vUv = repeat * uv;
vec4 mvPosition = modelViewMatrix * vec4(position, 1 );
gl_Position = projectionMatrix * mvPosition;
}
`;
var fragShader = `
uniform sampler2D textures[` + pics.length + `];
uniform float transition;
uniform float currentUniform;
uniform float nextUniform;
varying vec2 vUv;
vec4 getTexture(int index){
for(int i = 0; i < ` + pics.length + `; i++){
if (i == index){ return texture2D(textures[i],vUv); }
}
}
void main()
{
float chunk = 1. / ` + 1 + `.; // amount of transitions = 1
float t = floor(transition / chunk);
int idx0 = int(currentUniform);
int idx1 = int(nextUniform);
gl_FragColor = mix(
getTexture(idx0),
getTexture(idx1),
(transition - (float(t) * chunk)) * ` + 1 + `.
);
}
`;
window.onload=function()
{
init();
animate();
}
function init(){
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
controls = new THREE.TrackballControls( camera,renderer.domElement );
camera.position.z = 500;
console.log(camera.fov);
scene = new THREE.Scene();
scene.add(camera);
var magicPlaneMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vertShader,
fragmentShader: fragShader,
side: THREE.DoubleSide
});
for (var i = 0; i < Object.keys(textureData).length; i++) {
uniforms.textures.value[i].wrapS = uniforms.textures.value[i].wrapT = THREE.RepeatWrapping;
uniforms.textures.value[i].needsUpdate = true;
}
// for (var i = 0; i < Object.keys(textureData).length; i++) {
// uniforms.textures.value[i].wrapS = uniforms.textures.value[i].wrapT = THREE.RepeatWrapping;
// uniforms.textures.value[i].needsUpdate = true;
// }
var magicPlaneGeometry = new THREE.PlaneBufferGeometry(1000, 1000, 16, 16);
var magicPlaneMesh = new THREE.Mesh(magicPlaneGeometry, magicPlaneMaterial);
magicPlaneMesh.position.y = -500;
magicPlaneMesh.rotation.x = Math.PI / 2;
magicPlaneMesh.scale.x=10;
magicPlaneMesh.scale.y=10;
scene.add(magicPlaneMesh);
changeMagicPlane(currentTexture);
document.addEventListener( 'wheel', onDocumentMouseWheel, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseWheel( event ) {
var fov = camera.fov + event.deltaY * 0.05;
camera.fov = THREE.Math.clamp( fov, 10, 75 );
console.log(camera.fov);
camera.updateProjectionMatrix();
}
function animate() {
if(tweenUpdate=="true")
{
TWEEN.update();
}
renderer.render( scene, camera );
controls.update();
requestAnimationFrame( animate );
}
function changeMagicPlane(asset){
var assNum= parseInt(asset);
nextTexture = assNum;
uniforms.nextUniform.value = nextTexture;
console.log("Cuurent: "+currentTexture);
console.log("Next: "+nextTexture);
magicTween.start();
tweenUpdate="true";
tweenOver="false";
}
magicTween.onUpdate(function(){
uniforms.transition.value = magicPosition.magicTrans;
});
magicTween.onComplete(function(){
tweenUpdate="false";
tweenOver="true";
clicked="false";
//console.log("Am i complete?");
magicPosition.magicTrans=0;
currentTexture=nextTexture;
uniforms.currentUniform.value = currentTexture;
console.log("Current: "+currentTexture);
});
</script>
</body>
</html>
我正在尝试使用 ShaderMaterial 来实现淡入淡出效果。我的纹理图像是 256*256 像素。
工作片段。从这里使用 Tween.js ( http://learningthreejs.com/blog/2011/08/17/tweenjs-for-smooth-animation/ )。gman.png 来自这里(https://i.imgur.com/ZKMnXce.png)
解决方案
通过将纹理缩小函数 ( ) 设置为值,您已禁用三线性纹理过滤 ( mipmaps ) :.minFilter
THREE.LinearFilter
uniforms.textures.value[idx].minFilter = THREE.LinearFilter;
这会导致莫尔效应。
通过以下方式激活三线纹理过滤THREE.LinearMipMapLinearFilter
(这是默认设置):
uniforms.textures.value[idx].minFilter = THREE.LinearMipMapLinearFilter;
无论如何,您的(片段)着色器代码具有未定义的行为,并且 mip-mapping 将不起作用:
vec4 getTexture(int index){ for(int i = 0; i < ` + pics.length + `; i++){ if (i == index){ return texture2D(textures[i],vUv); } } } void main() { // .... gl_FragColor = mix( getTexture(idx0), getTexture(idx1), (transition - (float(t) * chunk)) * ` + 1 + `. );
请参阅OpenGL ES 着色语言 1.00 规范 - 13 致谢;第 107 页:
5 数组、向量和矩阵的索引
[...]
采样器
GLSL ES 1.00 支持采样器数组和包含采样器的结构数组。在这两种情况下,对于 ES 2.0,都要求支持使用常量索引表达式进行索引,但不要求支持使用其他值进行索引。
[...]
6 纹理访问
在非统一条件块的主体内访问 mip 映射纹理会给出未定义的值。非统一条件块是在编译时无法确定其执行的块。
在 main 的块范围内进行纹理查找,并使用constant-index-expression作为纹理采样器数组的索引:
例如
float a = transition - float(t) * chunk;
gl_FragColor = mix(texture2D(textures[0], vUv), texture2D(textures[1], vUv), a);
推荐阅读
- reactjs - 有没有办法在 Material UI 的评分组件中添加工具提示?
- .net-core - .net 核心 - Swagger - 将自定义参数添加到授权弹出窗口
- laravel - 使用 group by 并一起采摘?
- android - 设备启动时如何使用颤振启动后台服务
- sql - 在 SQL Server 中,如何识别“双”字符串并进行更正?
- reactjs - 如何在 useEffect 函数的异步函数中使用变量的“实时”值?
- mongodb - mongo 中的每日明智 + 小时分桶
- vba - 呼叫子和号码
- javascript - 代码仅在警报时才能正常工作。评论提醒其给予
- kubernetes - GKE 和 Graceful 节点关闭与 Shutdown 状态的 Pod 的区别