ThreeJS入门(九)纹理贴图
本帖最后由 马黑黑 于 2025-5-29 12:15 编辑 <br /><br /><style>.artBox { font-size: 18px; }
.artBox > p { margin: 10px 0; line-height: 30px; }
.artBox mark { background: lightblue; padding: 4px 8px; }
#prevBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: beige; display: none; padding: 0; overflow: hidden; z-index: 1000; 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提供足够的实现方式,其中,纹理贴图 <mark>Texture</mark> 是给材质表面添加图像或颜色,以此改进、丰富材质的外观属性。</p>
<p>贴图需要加载图像,ThreeJS内置有多种图像的加载方案,一般建议使用 <mark>TextureLoader()</mark> 加载器,它有一个加载方法 <mark>load(url) </mark> 用以加载图像资源(url地址):</p>
<div class="hEdiv"><pre class="hEpre">
// 创建 texture 加载器
var loader = new THREE.TextureLoader();
// 创建 texture 贴图
var texture = loader.load('图片地址');
// 上述写法等同于
var texture = new THREE.TextureLoader().load('图片地址');
</pre></div>
<p>这里有两个问题需要注意:其一,加载为 texture 纹理贴图的图片资源应是同源的或支持跨域的。同源指图片和网页同一个网站,支持跨域指来自不同网站的图片支持外链且支持跨源通信;其二,TextureLoader 加载器的 load() 加载方法是一个异步方法,这会产生图片加载尚未完成其余的进程却已经进入了工作状态的现象,处理不当贴图将无法成功渲染。ThreeJS意识到这个问题,所以,它为 load() 方法设置一个回调函数,以下是一个非标准的回调处理方案:</p>
<div class="hEdiv"><pre class="hEpre">
var texture = new THREE.TextureLoader().load('图片地址', () => {
// ... texture相关属性设置略
renderer.render(scene, camera); // 加载完毕渲染器渲染效果
});
</pre></div>
<p>上述方法核心是让渲染器做一次渲染,以确保贴图正常显示,实际上还应该加入一个出错处理的函数。当然,如果后续的渲染放在循环运行的动画中,做不做上述处理都无伤大雅,但还是建议使用回调函数处理相关设置和渲染问题。</p>
<p>纹理贴图本质上是一个相当复杂的机制,还有很多很多需要设置的细节。作为入门,学习并掌握本讲所介绍的内容就已经很不错了。下面给一个完整的示例,相关核心内容均在相应的注释中有简单说明:</p>
<div class="hEdiv"><pre id="pre1" class="hEpre">
<div style="margin: 10px; position: absolute;">点击页面暂停/继续动画</div>
<script type="module">
import * as THREE from 'https://638183.freep.cn/638183/web/ku/three.module.min.js';
const scene = new THREE.Scene;
const clock = new THREE.Clock();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const picUrl = 'https://638183.freep.cn/638183/small/cat1.jpg';
// 纹理加载器加载图片
const texture = new THREE.TextureLoader().load(picUrl, () => {
texture.colorSpace = THREE.SRGBColorSpace; // 保真
texture.wrapS = THREE.RepeatWrapping; // 横向重复开启
texture.wrapT = THREE.RepeatWrapping; // 纵向重复开启
texture.repeat.set(1, 1); // 重复次数纵横方向各为 1
//renderer.render(scene, camera); // 渲染效果或直接运行动画
animate();
});
// 创建立方体 :使用 map 属性给材质添加纹理,材质的 color 和纹理颜色叠加
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 1.5, 1.5),
new THREE.MeshBasicMaterial({ color: 0x00ffee, map: texture })
);
scene.add(mesh);
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mesh.rotation.x += delta / 2;
mesh.rotation.y += delta / 3;
renderer.render(scene, camera);
};
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
document.onclick = () => clock.running ? clock.stop() : clock.start();
//animate(); // 纹理加载时已经运行动画的话这里就不用重复运行
</script>
</pre></div>
<blockquote><button id="btnPrev1">运行代码</button></blockquote>
<p>以上是老师直播带货的主打产品六只猫苦咖啡包装盒原型,六只猫苦咖啡系支农产品,通过老师讲堂下单所得收益老师分文不取。感谢大家的爱心,有你,咖啡再苦,也苦不了山沟沟里的孩纸。</p>
<p>附:<a href="http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/textures/Texture" target="_blank">ThreeJS中文网·纹理(Texture)</a></p>
</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 value1 = pre1.textContent;
btnPrev1.onclick = () => preView(value1, prevBox);
</script> 高科技技术{:4_178:}虽然我看不懂,但很佩服黑黑老师的才能,给老师点赞!{:4_180:} 去看了链接,发现纹理帖子的确需要考虑很多东西。
看了样例,不知道是怎么决定图片方向的,不过这样的转动下,总有可能出现图像是反向的面{:4_173:} 这个和帖子里插张图图完全不同,需要考虑的太多了。嗯嗯,能学会介绍的内容已经很不错了。
感谢黑黑的讲解,原来漂亮的贴图是这样这样弄出来的{:4_187:} 之前在笼和回声作品里都见过给几何体贴图的范例,当时只知道换个地址{:4_173:}
现在拎出来细讲,更清楚了。
原来是使用加载器来加载图片,还要注意同源。。跨域要开放的地址才可以。。
遵守它的规则还是很必要的。。 花飞飞 发表于 2025-5-29 20:26
之前在笼和回声作品里都见过给几何体贴图的范例,当时只知道换个地址
现在拎出来细讲,更清楚了 ...
我试了一下,绝大多数图片来源其实都支持信息交流 红影 发表于 2025-5-29 15:16
这个和帖子里插张图图完全不同,需要考虑的太多了。嗯嗯,能学会介绍的内容已经很不错了。
感谢黑黑的讲解 ...
本身不算太复杂 包装上的猫猫很漂亮,也慵懒~~打个招呼~~
苦咖啡香醇,回味悠长{:4_173:} 红影 发表于 2025-5-29 15:15
去看了链接,发现纹理帖子的确需要考虑很多东西。
看了样例,不知道是怎么决定图片方向的,不过这样的转动 ...
纹理是在一个几何体图像上贴图,不止是往一个平面上那么简单,理解它,必须不能局限于对平面的认知。 菲儿 发表于 2025-5-29 14:54
高科技技术虽然我看不懂,但很佩服黑黑老师的才能,给老师点赞!
谢点 花飞飞 发表于 2025-5-29 20:29
包装上的猫猫很漂亮,也慵懒~~打个招呼~~
苦咖啡香醇,回味悠长
好喝多喝 马黑黑 发表于 2025-5-29 20:27
我试了一下,绝大多数图片来源其实都支持信息交流
现在用的随意云基本都可以。。
聚合图片时显时不显的,估计不太行。
花潮里我没传过大图。。不晓得是否可以外面显示
我发现有些地方的图片网站可以,别的坛子就不太行。。{:4_173:} 马黑黑 发表于 2025-5-29 20:31
好喝多喝
你说的好喝也要适量啊{:4_173:} 花飞飞 发表于 2025-5-29 20:33
你说的好喝也要适量啊
多喝就是适量的 花飞飞 发表于 2025-5-29 20:33
现在用的随意云基本都可以。。
聚合图片时显时不显的,估计不太行。
花潮里我没传过大图。。不晓得是否 ...
论坛基本都不允许外链,就别想了 马黑黑 发表于 2025-5-29 21:07
多喝就是适量的
{:4_173:}这道理是正理 花飞飞 发表于 2025-5-29 20:33
现在用的随意云基本都可以。。
聚合图片时显时不显的,估计不太行。
花潮里我没传过大图。。不晓得是否 ...
{:4_173:}环境侧重点不同吧。大图还是图床的好 马黑黑 发表于 2025-5-29 20:29
本身不算太复杂
是的,看完讲义更清楚了{:4_187:} 马黑黑 发表于 2025-5-29 20:30
纹理是在一个几何体图像上贴图,不止是往一个平面上那么简单,理解它,必须不能局限于对平面的认知。
但很难理解,不清楚是如何分的起点位置。 红影 发表于 2025-5-29 21:53
是的,看完讲义更清楚了
实际上,ThreeJS里的示例,基本不用回调函数,这是因为渲染放在了动画系统里了,后续总能渲染出来