ThreeJS入门(三)场景配色和自适应窗口
本帖最后由 马黑黑 于 2025-5-23 07:53 编辑 <br /><br /><style>.artBox { font-size: 18px; }
.artBox > p { margin: 10px 0; }
#prevBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: rgba(255,255,255,.95); display: none; padding: 0; overflow: hidden; z-index: 100; margin: 0; }
#prevBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
</style>
<div id="prevBox"></div>
<div class="artBox">
<p>在第二讲,如果运行示例代码会发现,整个 ThreeJS 的背景是黑色的,并且,当浏览器窗口向下还原或以其它方式改变尺寸时,3d立方体并没有处在它应该呆在的原始位置甚至可能看不见它。这涉及到场景配色和自适应窗口问题,本讲专门介绍的核心内容。</p>
<h2>一、场景配色</h2>
<p>取消黑色背景,简单的方法是在创建渲染器 renderer 之时开启 alpha 通道:</p>
<div class="hEdiv"><pre class="hEpre">
//var renderer = new THREE.WebGLRenderer({ antialias: true }); // 改为
var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
</pre></div>
<p>或者,不论是否开启了渲染器的 alpha 通道,我们都可以单独设置渲染器的背景颜色:</p>
<div class="hEdiv"><pre class="hEpre">
var renderer = new THREE.WebGLRenderer({ antialias: true }); // 不开启alpha通道
//var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); // 开启alpha通道
renderer.setClearColor(0xffeecc); // 设置渲染器背景颜色
//renderer.setClearColor('#ffeecc'); // 使用传统颜色表达法
// 说明:0x 是 ThreeJS 颜色的前缀,后面加十六进制颜色值,不需要引号;也可以使用其它颜色表达方法,需要使用引号。
</pre></div>
<p>还可以直接操作场景 scene 的 background 背景:</p>
<div class="hEdiv"><pre class="hEpre">
var scene = new THREE.Scene; // 创建场景
scene.background = new THREE.Color(0xffeecc); // 设置场景颜色
</pre></div>
<p>一般的建议是,如果是做帖需要用到 ThreeJS,直接开启渲染器 alpha 通道最好,其它不用管,这样可以令 ThreeJS 特效和帖子背景等完美配套。至于选取其它的配色方案则取决于创作和其他方面的需求。应该提一下的是,一些特殊情形下,3d作品可能不是开启 alpha 通道或设置渲染器背景、场景背景就可以处理的,这时候可以考虑配色时使用具有 alpha 通道的颜色,如 rgba 等,也可以考虑给 canvas 配置透明度。</p>
<h2>二、自适应窗口</h2>
<p>这需要用到 JS 对 window 的 resize 监听事件,resize 指尺寸变更之意。当窗口发生变化,我们需要考虑相机和渲染器的相关问题:一是相机视口的宽高比要跟着变化,同时同步更新相机的投影矩阵,二是重设渲染器的渲染范围。看代码:
<div class="hEdiv"><pre class="hEpre">
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight; // 更新相机视口
camera.updateProjectionMatrix(); // 同步相机投影矩阵
renderer.setSize(window.innerWidth, window.innerHeight); // 重设渲染器渲染范围
renderer.render(scene, camera); // 重新渲染效果
}
</pre></div>
<p>这几行代码基本可以适用于任何场合,可能需要改变的仅仅是 window.innerWidth 和 window.innerHeight,比如根据场景改为父 div 元素的 offsetWidth 和 offsetHeight,或者 clientWidth 和 clientHeight。另外,两个细节应该提一下,一,相机的相关更新在一些简单的场合下可以忽略,二,渲染器重新渲染的指令是否需要视动画设计而定,如果动画中运行有渲染指令,这里就不必重复。</p>
<p>下面的代码,在第二讲示例的基础上加入了背景设置、窗口自适应,可以在线运行:</p>
<div class="hEdiv"><pre class="hEpre">
<script type="module">
import * as THREE from 'https://unpkg.ihwx.cn/three@0.176.0/build/three.module.js';
var scene = new THREE.Scene;
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 5);
camera.lookAt(0, 0, 0);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor(0xffeecc);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var geometry = new THREE.BoxGeometry();
var material = new THREE.MeshNormalMaterial();
var mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(0.5);
mesh.rotateY(0.5);
mesh.rotateZ(-0.5);
scene.add(mesh);
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight; // 更新相机视口
camera.updateProjectionMatrix(); // 同步相机投影矩阵
renderer.setSize(window.innerWidth, window.innerHeight); // 重设渲染器渲染范围
renderer.render(scene, camera); // 重新渲染效果
}
renderer.render(scene, camera);
</script>
</pre></div>
<blockquote><button id="btnPrev">运行代码</button></blockquote>
</div>
<script type="module">
import hlight from 'https://638183.freep.cn/638183/web/helight/helight1.js';
const pres = document.querySelectorAll('.hEpre');
const divs = document.querySelectorAll('.hEdiv');
divs.forEach( (div, key) => hlight.hl(div, pres));
const preView = (htmlCode, targetBox) => {
if (targetBox.innerHTML) return;
const iframe = document.createElement('iframe');
htmlCode = htmlCode + '<style>body {margin: 0; }</style>';
iframe.srcdoc = htmlCode;
targetBox.appendChild(iframe);
targetBox.style.display = 'block';
targetBox.onclick = () => {
targetBox.innerHTML = '';
targetBox.style.display = 'none';
}
};
const value = pres.textContent;
btnPrev.onclick = () => preView(value, prevBox);
</script> 早上问好黑黑老师,看到了一只有三种颜色的立方体。汇报完毕!{:4_173:} 辛苦了!谢谢马老师,讲的太棒了{:4_191:}
不清楚什么时候需要改以及怎样改。请老师进一步讲解一下“可能需要改变的仅仅是 window.innerWidth 和 window.innerHeight,比如根据场景改为父 div 元素的 offsetWidth 和 offsetHeight,或者 clientWidth 和 clientHeight。”谢谢老师{:4_190:} 原来更换背景可以是设置渲染器背景,也可以直接更换场景背景,这个是演示用的。
做帖子时肯定开启渲染器 alpha 通道,让效果和想要的背景融合了{:4_187:} 自适应窗口也是很重要的,可以知道在什么范围对ThreeJS几何体进行运作了。
感谢黑黑的耐心讲解,这些讲解真好{:4_199:} 杨帆 发表于 2025-5-23 09:27
辛苦了!谢谢马老师,讲的太棒了
不清楚什么时候需要改以及怎样改。请老师进一步讲解一下“可 ...
是不是把window.innerWidth改为div.offsetHeight?高也一样。
我也不清楚,只是猜的{:4_173:} 红影 发表于 2025-5-23 10:47
是不是把window.innerWidth改为div.offsetHeight?高也一样。
我也不清楚,只是猜的
呵呵,不去猜了,等老师答疑解惑吧{:4_204:} 杨帆 发表于 2025-5-23 09:27
辛苦了!谢谢马老师,讲的太棒了
不清楚什么时候需要改以及怎样改。请老师进一步讲解一下“可 ...
这个要看你的应用环境,碰上了你才能体会到 梦江南 发表于 2025-5-23 08:17
早上问好黑黑老师,看到了一只有三种颜色的立方体。汇报完毕!
{:4_190:} 红影 发表于 2025-5-23 10:42
原来更换背景可以是设置渲染器背景,也可以直接更换场景背景,这个是演示用的。
做帖子时肯定开启渲染器 a ...
{:4_181:} 红影 发表于 2025-5-23 10:45
自适应窗口也是很重要的,可以知道在什么范围对ThreeJS几何体进行运作了。
感谢黑黑的耐心讲解,这些讲解 ...
{:4_190:} 红影 发表于 2025-5-23 10:47
是不是把window.innerWidth改为div.offsetHeight?高也一样。
我也不清楚,只是猜的
比较一下之前各帖子的代码不就一切明了了{:4_173:} 马黑黑 发表于 2025-5-23 12:37
这个要看你的应用环境,碰上了你才能体会到
谢谢老师解答。依据应用环境去选择,这就难了{:4_173:} 杨帆 发表于 2025-5-23 13:00
谢谢老师解答。依据应用环境去选择,这就难了
一切在应用中 红影 发表于 2025-5-23 10:45
自适应窗口也是很重要的,可以知道在什么范围对ThreeJS几何体进行运作了。
感谢黑黑的耐心讲解,这些讲解 ...
{:4_191:} 背景颜色有两种设置方法,还可以进行透明度设置,这个好详细了。。做贴实用。。{:4_199:} innerWidth offsetWidthclientWidth出现不同的宽高表示方法,
适用于不同的父元素场景,啥时候用哪一个呢。。{:4_173:} 窗口自适应单独拎出来讲真是太好了。。
之前看教程范例的时候就觉得是自然而然的,把窗口小化一些,几何体自然就跟着缩小。。
现在看来是经过精心设计出现的效果。。
把入门二和这个对比一下,更加明显了。。{:4_199:}
白老师出的每个教程都细致到无人可以匹敌,大赞。。 杨帆 发表于 2025-5-23 10:50
呵呵,不去猜了,等老师答疑解惑吧
嗯嗯,我也没学好{:4_173:}