threejs:着色器onBeforeCompile给导入的模型添加光带扫描效果

模型材质属性丢失

上一篇博客我们学习了用着色器给模型添加光带扫描效果,今天来学习给导入的模型添加光带扫描效果,目标是给如下图的立筒仓加光带扫描。

首先我们试试原来的方法还是否有效。

import * as THREE from 'three';// 引入gltf模型加载库GLTFLoader.js
import {GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';export const model = new THREE.Group();
const loader = new GLTFLoader();const vertexShader = `
varying vec2 vPosition;
void main() {vPosition = position; // 将UV坐标传递给片元着色器gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;const fragmentShader = `
varying vec2 vPosition;void main() {if(vPosition.y > 10 && vPosition.y < 11){gl_FragColor = vec4(0.0,1.0,1.0,1.0);}}
`;// 以下代码是使用着色器材料进行颜色设置
export const material = new THREE.ShaderMaterial({//顶点着色器对象vertexShadervertexShader: vertexShader,// 片元着色器对象fragmentShaderfragmentShader: fragmentShader,
});loader.load('models/粮仓.glb', function (gltf) {let parentObj = gltf.scene.getObjectByName("立筒仓");parentObj.traverse(function (obj) {if (obj.isMesh) {console.log(obj.material);obj.material = material;}});model.add(gltf.scene);
});

运行结果,模型没法显示了。

因为模型贴图了,得用uv而不是position了,uv只包含xy两个数据项,范围是0~1.

const vertexShader = `
varying vec2 vUv;
void main() {vUv = uv; // 将UV坐标传递给片元着色器gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;const fragmentShader = `
varying vec2 vUv;void main() {if(vUv.y > 0.5 && vUv.y < 0.6){gl_FragColor = vec4(1.0,1.0,0.0,1.0);}}
`;

运行后可以看到模型和光带了,但模型变成了白色,这是因为我们直接把模型本身的材质硬生生换成了ShaderMaterial,原本材质的属性就都丢失了。

 onBeforeCompile的用法

是时候让onBeforeCompile出马了,这个方法在保留模型本身材质的前提下,对材质进行修改。

ShaderLib下,打开一个mesh材质的js文件看看顶点着色器和片元着色器的代码,它们都有void main()方法,我们主要是要修改这里。

修改顶点着色器

在片元着色器里,要在main的前面加varying vec2 vUv; 

在main的里面加

vUv = uv; // 将UV坐标传递给片元着色器

gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

使用replace方法对顶点着色器字符串进行修改,需要注意replace第二个参数里的main方法,不需要加},因为我们只是在main方法里插入

vUv = uv; // 将UV坐标传递给片元着色器

gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

这些代码,main方法里面还有很多内置代码,main方法并没有在这里结束。

顶点着色器比较容易修改,内容不多。

shader.vertexShader = shader.vertexShader.replace(`void main() {`,`varying vec2 vUv;void main() {vUv = uv; // 将UV坐标传递给片元着色器gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);`);

修改片元着色器

片元着色器的修改相对来说要复杂一点,片元着色器里的varying vec2 vUv;跟刚才一样,加在main方法的前面即可。

shader.fragmentShader = shader.fragmentShader.replace(`void main() {`,`varying vec2 vUv;void main() {`);

后面的条件判断等代码,必须要加到片元着色器main方法最后一行代码的后面,比如这里最后一行代码是#include <dithering_fragment>,不同版本的threejs,不同材质,main方法最后一行代码不同,一定打印出来看看。console.log(shader.fragmentShader);

现在我们确定了main方法最后一行是#include <dithering_fragment>,还是使用replace往它后面插入if判断这些代码。片元着色器用了两次replace。

shader.fragmentShader = shader.fragmentShader.replace(`#include <dithering_fragment>`,`#include <dithering_fragment>if(vUv.y > 0.5 && vUv.y < 0.6){gl_FragColor = vec4(1.0,1.0,0.0,1.0);}`);

如果片元着色器第二次replace的内容太多,可以放在一个js文件里,引入。

fragment_shader.js

需要特别注意,fragment_shader.js文件开头必须跟刚才main方法最后一行相同,也就是还得带着#include <dithering_fragment>这句话,不然就相当于这句话被去掉了,代码就不完整了。

export default /* glsl */
`
#include <dithering_fragment>
// 整体设置渐变半透明色
gl_FragColor = vec4(mix(vec3(1.0, 1.0, 1.0),vec3(1.0, 0.0, 1.0),vPosition.z/20.0),0.5);// gl_FragColor = vec4(1.0,0.0,0.0,0.5);// 符合vPosition.y > y0 && vPosition.y < y0+1.0条件的设置其他颜色,形成光带
float y0 = 0.0;
for(int i=0;i<4;i++){y0 +=4.0;if(vPosition.z > y0 && vPosition.z < y0+1.0){gl_FragColor = vec4(1.0,1.0,0.0, 1.0);}
}`;
import fragment_shader from './fragment_shader.js'
shader.fragmentShader = shader.fragmentShader.replace('#include <dithering_fragment>', fragment_shader);

认识uv坐标

运行之后,光带有了,但是方向跟我们预期的不同,我们预期水平光带,结果变成了垂直的。

在blender里选中模型,打开uv编辑,可以下载该模型使用的贴图。

把贴图放到uv坐标里,是这样了。u就相当于x轴,v就相当于y轴,对照下图来看,光带应该从左到右移动,需要处理的是y轴数值,而不是x轴。

shader.fragmentShader = shader.fragmentShader.replace(`#include <dithering_fragment>`,`#include <dithering_fragment>if(vUv.x > 0.5 && vUv.x < 0.55){gl_FragColor = vec4(1.0,1.0,0.0,1.0);}`);

 现在光带方向正常了,要让它动起来,上一篇博客已经写过了,要使用uniforms传值,onBeforeCompile方法里的shader是内部对象,另外定义一个外部变量将它传出去,然后在渲染函数中改变它,但是,马上就报错了,说找不到uniforms属性,这里又是一个注意点,onBeforeCompile内部的shader在第一次render执行的时候,还没有值,所以取不到uniforms,render运行多次后,shader才有值。为了确保取到值,要加非空判断。

export let materialShader = null;
shader.uniforms.x = {value:0.0};
materialShader = shader; //将内部的shader传递到外部
function render() {renderer.render(scene, camera);// materialShader刚开始没有值,随着render的运行几次后才有,所以要加if判断,否则会报错if(materialShader){materialShader.uniforms.x.value += 0.01;if(materialShader.uniforms.x.value > 1.0){materialShader.uniforms.x.value = 0.0;}}requestAnimationFrame(render);
}

光带到顶部,会改变方向,这个问题我们上一篇博客也遇到过类似情况,将扫描范围缩小一点就可以了,比如materialShader.uniforms.x.value > 1.0的时候,重置到0。

图片尺寸是512*512,光带扫到了顶部,其实对应图片右边的两个白色圆片,假如只需要扫描左边的部分,用截图工具测量一下长度是372,372/512约等于0.72,修改materialShader.uniforms.x.value > 0.7,重置到0,光带就不会扫到顶部了。把光带变成多表彩色的,上一篇博客里讲过了方法。

完整代码:

import * as THREE from 'three';// 引入gltf模型加载库GLTFLoader.js
import {GLTFLoader
} from 'three/addons/loaders/GLTFLoader.js';export const model = new THREE.Group();
const loader = new GLTFLoader();
export let materialShader = null;
loader.load('models/粮仓.glb', function (gltf) {let parentObj = gltf.scene.getObjectByName("立筒仓");parentObj.traverse(function (obj) {if (obj.isMesh) {    obj.material.onBeforeCompile = function (shader) {console.log(shader.fragmentShader);shader.vertexShader = shader.vertexShader.replace(`void main() {`,`varying vec2 vUv;void main() {vUv = uv; // 将UV坐标传递给片元着色器gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);`);shader.fragmentShader = shader.fragmentShader.replace(`void main() {`,`varying vec2 vUv;uniform float x;void main() {`);shader.fragmentShader = shader.fragmentShader.replace(`#include <dithering_fragment>`,`#include <dithering_fragment>if(vUv.x > x && vUv.x < x+0.02){gl_FragColor = vec4(1.0,1.0,0.0,1.0);}`);shader.uniforms.x = {value:0.0};materialShader = shader; //将内部的shader传递到外部}}});model.add(gltf.scene);
});

// 渲染循环
function render() {renderer.render(scene, camera);// materialShader刚开始没有值,随着render的运行几次后才有,所以要加if判断,否则会报错if(materialShader){materialShader.uniforms.x.value += 0.01;if(materialShader.uniforms.x.value > 1.0){materialShader.uniforms.x.value = 0.0;}}requestAnimationFrame(render);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29033.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python----数据分析(Matplotlib五:pyplot的其他函数,Figure的其他函数, GridSpec)

一、pyplot的其他函数 1.1、xlabel 在matplotlib中&#xff0c; plt.xlabel() 函数用于为当前活动的坐标轴&#xff08;Axes&#xff09;设置x轴的 标签。当你想要标识x轴代表的数据或单位时&#xff0c;这个函数非常有用。 plt.xlabel(xlabel text) 1.2、ylabel 在matplotl…

基于GeoTools的GIS专题图自适应边界及高宽等比例生成实践

目录 前言 一、原来的生成方案问题 1、无法自动读取数据的Bounds 2、专题图高宽比例不协调 二、专题图生成优化 1、直接读取矢量数据的Bounds 2、专题图成果抗锯齿 3、专题成果高宽比例自动调节 三、总结 前言 在当今数字化浪潮中&#xff0c;地理信息系统&#xff08;…

aardio - 虚表 + 数据库 操作例程

import godking.vlistEx; import fonts.fontAwesome import win.ui; /*DSG{{*/ mainForm win.form(text"客户信息管理";right967;bottom556;border"none") mainForm.add( addData{cls"plus";text\uF067 新增;left8;top80;right77;bottom110;bgc…

SQLAlchemy系列教程:理解SQLAlchemy元数据

SQLAlchemy是Python开发人员的强大ORM工具。SQLAlchemy中的元数据是对象-关系映射配置的集合&#xff0c;允许开发人员无缝地定义和使用数据库模式。 使用元数据 SQLAlchemy中的元数据充当各种数据库描述符&#xff08;如表、列和索引&#xff09;的容器。这使开发人员能够通…

Soul 1.4.60 | 清爽版浏览器,内置广告拦截与多功能集成,保护隐私

Soul浏览器是一款专注于内容浏览体验的安卓浏览器&#xff0c;去除广告与追踪器&#xff0c;集成视频下载、PDF查看、手势控制等实用功能。支持无痕模式与黑暗主题&#xff0c;内置清洁器自动过滤广告&#xff0c;优化网页加载速度&#xff0c;提供流畅的浏览体验与隐私保护。支…

最短路问题

Problem - D - Codeforces&#xff08;最短路&#xff0c;反向bfs&#xff09; 题目&#xff1a; 思路&#xff1a; bfs版本&#xff1a;参考自Codeforces Round 1002 (Div. 2) A - D - 知乎 代码&#xff1a; dijstra&#xff1a; void solve() {int n;cin>>n;int s…

【论文阅读】多模态——LSeg

文献基本信息 标题&#xff1a;Language-Driven Semantic Segmentation作者&#xff1a;Boyi Li、Kilian Q. Weinberger、Serge Belongie、Vladlen Koltun、Ren Ranftl单位&#xff1a;Cornell University、University of Copenhagen、Apple、Intel Labs会议/期刊&#xff1a;…

Docker Desktop常见问题记录

1.docker pull报错&#xff0c;无法连接https://registry-1.docker.io/v2/ 报错信息如下&#xff1a; Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection(Client.Timeout exceeded …

Java 大视界 -- Java 大数据在智能政务公共服务资源优化配置中的应用(118)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

Python快捷手册

Python快捷手册 后续会陆续更新Python对应的依赖或者工具使用方法 文章目录 Python快捷手册[toc]1-依赖1-词云小工具2-图片添加文字3-BeautifulSoup网络爬虫4-Tkinter界面绘制5-PDF转Word 2-开发1-多线程和队列 3-运维1-Requirement依赖2-波尔实验室3-Anaconda3使用教程4-CentO…

Javaweb后端spring事务管理 事务四大特性ACID

2步操作&#xff0c;只能同时成功&#xff0c;同时失败&#xff0c;要放在一个事务中&#xff0c;最后提交事务或者回滚事务 事务控制 事务管理进阶 事务的注解 这是所有异常都会回滚 事务注解 事务的传播行为 四大特性

AI绘画软件Stable Diffusion详解教程(2):Windows系统本地化部署操作方法(专业版)

一、事前准备 1、一台配置不错的电脑&#xff0c;英伟达显卡&#xff0c;20系列起步&#xff0c;建议显存6G起步&#xff0c;安装win10或以上版本&#xff0c;我的显卡是40系列&#xff0c;16G显存&#xff0c;所以跑大部分的模型都比较快&#xff1b; 2、科学上网&#xff0…

光伏电池输出功率模型

1.光伏电池输出功率 1.1光伏电池的效率 温度对光伏电池/组件电效率的影响可以追溯到温度对电流I和电压V的影响&#xff0c;因为最大功率表达式为&#xff1a; 其中&#xff0c;Pm为最大输出功率&#xff1b;Vm为最大输出功率点电压&#xff1b;Im为最大输出功率点电流&#xf…

【大模型基础_毛玉仁】1.4 语言模型的采样方法

【大模型基础_毛玉仁】1.4 语言模型的采样方法 1.4 语言模型的采样方法1.4.1 概率最大化方法1&#xff09;贪心搜索&#xff08;GreedySearch&#xff09;2&#xff09;波束搜索&#xff08;BeamSearch&#xff09; 1.4.2 随机采样方法1&#xff09;Top-K 采样2&#xff09;Top…

MyBatis - XML CRUD 其他查询

1. XML 配置文件 使用 MyBatis 操作数据库的方式有两种: 注解 (在注解中定义 SQL 语句)XML 配置文件 (在 XML 文件中定义 SQL 语句) 在上一篇博客中, 已经讲解了如何使用注解操作数据库, 本篇文章来讲解如何使用 XML 进行 MyBatis 开发. 使用 XML 的步骤, 和使用注解的步骤…

DeepSeek + 飞书多维表格搭建你的高效工作流

众所周知&#xff0c;大模型DeepSeek擅长于处理大规模语言模型推理任务&#xff0c;特别是在成本降低和思维链推理方面表现出色‌&#xff0c;我们一般把大模型必做我们的大脑&#xff0c;但是一个人不能只有大脑&#xff0c;还需要其他输入输出以及操作支配的眼耳鼻嘴手足等。…

跨域-告别CORS烦恼

跨域-告别CORS烦恼 文章目录 跨域-告别CORS烦恼[toc]1-参考网址2-思路整理1-核心问题2-个人思考3-脑洞打开4-个人思考-修正版1-个人思考2-脑洞打开 3-知识整理1-什么是跨域一、同源策略简介什么是源什么是同源是否是同源的判断哪些操作不受同源策略限制跨域如何跨域 二、CORS 简…

基于Django创建一个WEB后端框架(DjangoRestFramework+MySQL)流程

一、Django项目初始化 1.创建Django项目 Django-admin startproject 项目名 2.安装 djangorestframework pip install djangorestframework 解释: Django REST Framework (DRF) 是基于 Django 框架的一个强大的 Web API 框架&#xff0c;提供了多种工具和库来构建 RESTf…

基于多目标向日葵优化算法(Multi-objective Sunflower Optimization,MOSFO)的移动机器人路径规划研究,MATLAB代码

一、机器人路径规划介绍 移动机器人路径规划是机器人研究的重要分支&#xff0c;是对其进行控制的基础。根据环境信息的已知程度不同&#xff0c;路径规划分为基于环境信息已知的全局路径规划和基于环境信息未知或局部已知的局部路径规划。随着科技的快速发展以及机器人的大量…

cursor使用经验分享(java后端服务开发向)

前言 cursor是一款基于vscode&#xff0c;并集成AI能力的代码编辑器&#xff0c;其功能包括但不限于代码生成及补全、AI对话&#xff08;能够直接将代码环境作为上下文&#xff09;、即时应用建议等等&#xff0c;是一款面向未来的代码编辑器。 对于vscode&#xff0c;最先想…