当前位置: 首页 > news >正文

【Three.js基础学习】29.Hologram Shader

前言

three.js 通过着色器如何实现全息影像,以及一些动态的效果。

一些难点的思维,代码目录

下面图是摄像机视角观看影响上的时候,如何实现光影的渐变,透视以及叠加等。

一、代码

1.index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Holographic</title><link rel="stylesheet" href="./style.css">
</head>
<body><canvas class="webgl"></canvas><script type="module" src="./script.js"></script>
</body>
</html>

2.style.css

*
{margin: 0;padding: 0;
}html,
body
{overflow: hidden;
}.webgl
{position: fixed;top: 0;left: 0;outline: none;
}

3.script.js

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import GUI from 'lil-gui'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import holographicVertexShader from './shaders/holographic/vertex.glsl'
import holographicFragmentShader from './shaders/holographic/fragment.glsl'/*** Base*/
// Debug
const gui = new GUI()// Canvas
const canvas = document.querySelector('canvas.webgl')// Scene
const scene = new THREE.Scene()// Loaders
const gltfLoader = new GLTFLoader()/*** Sizes*/
const sizes = {width: window.innerWidth,height: window.innerHeight
}window.addEventListener('resize', () =>
{// Update sizessizes.width = window.innerWidthsizes.height = window.innerHeight// Update cameracamera.aspect = sizes.width / sizes.heightcamera.updateProjectionMatrix()// Update rendererrenderer.setSize(sizes.width, sizes.height)renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})/*** Camera*/
// Base camera
const camera = new THREE.PerspectiveCamera(25, sizes.width / sizes.height, 0.1, 100)
camera.position.set(7, 7, 7)
scene.add(camera)// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true/*** Renderer*/
const rendererParameters = {}
rendererParameters.clearColor = '#1d1f2a'const renderer = new THREE.WebGLRenderer({canvas: canvas,antialias: true
})
renderer.setClearColor(rendererParameters.clearColor)
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))gui.addColor(rendererParameters, 'clearColor').onChange(() =>{renderer.setClearColor(rendererParameters.clearColor)})/*** Material*/
const materialParameters = {}
materialParameters.color = '#70c1ff'gui.addColor(materialParameters,'color').onChange(()=>{material.uniforms.uColor.value.set(materialParameters.color)})const material = new THREE.ShaderMaterial({vertexShader:holographicVertexShader,fragmentShader:holographicFragmentShader,uniforms:{uTime:new THREE.Uniform(0),uColor:new THREE.Uniform(new THREE.Color(materialParameters.color))},transparent:true, // 设置透明材质side:THREE.DoubleSide,depthWrite:false, // 停止深度写入blending:THREE.AdditiveBlending, // 混合叠加 叠加起来的颜色更亮})/*** Objects*/
// Torus knot
const torusKnot = new THREE.Mesh(new THREE.TorusKnotGeometry(0.6, 0.25, 128, 32),material
)
torusKnot.position.x = 3
scene.add(torusKnot)// Sphere
const sphere = new THREE.Mesh(new THREE.SphereGeometry(),material
)
sphere.position.x = - 3
scene.add(sphere)// Suzanne
let suzanne = null
gltfLoader.load('./suzanne.glb',(gltf) =>{suzanne = gltf.scenesuzanne.traverse((child) =>{if(child.isMesh)child.material = material})scene.add(suzanne)}
)/*** Animate*/
const clock = new THREE.Clock()const tick = () =>
{const elapsedTime = clock.getElapsedTime()// Update material.uniforms.uTime.value = elapsedTime;// Rotate objectsif(suzanne){suzanne.rotation.x = - elapsedTime * 0.1suzanne.rotation.y = elapsedTime * 0.2}sphere.rotation.x = - elapsedTime * 0.1sphere.rotation.y = elapsedTime * 0.2torusKnot.rotation.x = - elapsedTime * 0.1torusKnot.rotation.y = elapsedTime * 0.2// Update controlscontrols.update()// Renderrenderer.render(scene, camera)// Call tick again on the next framewindow.requestAnimationFrame(tick)
}tick()

4.fragment.glsl 顶点着色器


uniform float uTime; 
uniform vec3 uColor;varying vec3 vPosition;
varying vec3 vNormal;/* mod(X, Y)函数返回X对Y取模的结果。如果X和Y都是数组,mod函数会对数组的每个元素分别进行取模运算对上一步的结果取模 1.0,即计算该值除以 1.0 的余数。由于取模 1.0 的效果是将值限制在 0 到 1 之间(不包括1),这个操作实际上是在做一个周期性变换,使得 vUv.x 在 0 到 10 之间变化时,结果会在 0 到 1 之间循环pow(x,y)x的y次方。如果x小于0,结果是未定义的。同样,如果x=0并且y<=0,结果也是未定义的normalize 标准化向量,返回一个方向和x相同但长度为1的向量dot 向量x,y之间的点积Fresnel 根据相机角度,进行判断,相同方向1,直角0 ,相反 1同样法线应该渲染,vertex.glsl
*/void main(){// Normal 法线标准值重新定义为1vec3 normal = normalize(vNormal); // 两个角度大的法线之间,的法线长度不是标准,重新初始化1if(!gl_FrontFacing)normal *= - 1.0;// Stripes 条纹 float stripes = mod((vPosition.y - uTime * 0.02) * 20.0, 1.0);stripes = pow(stripes, 3.0);// Fresnel 菲涅尔效果 全息vec3 viewDirection = normalize(vPosition - cameraPosition) ; // 模型位置 - 相机位置,得到视图位置float fresnel = dot(viewDirection,normal) + 1.0; // 这里加上一,原本的0变成1,-1变成0fresnel = pow(fresnel,2.0);// Falloff 平滑重映射  想要实现边界处 1-0转变 逐渐消失的效果float falloff = smoothstep(0.8,0.0,fresnel);// Holographic 全息float holographic = stripes * fresnel;holographic += fresnel * 1.25;holographic *= falloff;// Final Colorgl_FragColor = vec4(uColor,holographic);#include <tonemapping_fragment>#include <colorspace_fragment>
}

5.vertex.glsl 片段着色器

uniform float uTime; varying vec3 vPosition;
varying vec3 vNormal;/* smoothstep(edge0, edge1, x) 如果x <= edge0,返回0.0 ;如果x >= edge1 返回1.0;如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。如果edge0 >= edge1,结果是未定义的。
*/#include ../includes/random2D.glslvoid main(){// Positionvec4 modelPosition = modelMatrix * vec4(position,1.0);// Glitch  random2D函数会给我0-1的数据,减去0.5 相机的中心位置就不会偏移float glitchTime = uTime - modelPosition.y; // 设置故障时间float glitchStrength = sin(glitchTime) +  sin(glitchTime * 3.45) +  sin(glitchTime * 8.76); // 改变频率glitchStrength /= 3.0;glitchStrength = smoothstep(0.3,1.0,glitchStrength);glitchStrength *= 0.25;modelPosition.x += (random2D(modelPosition.xz + uTime) - 0.5) * glitchStrength;modelPosition.z += (random2D(modelPosition.zx + uTime) - 0.5) * glitchStrength;gl_Position = projectionMatrix * viewMatrix * modelPosition;// Model normal/* 当第四个值为1.0时,向量为“同质”,所有3个变换(翻译、旋转、比例)都将被应用。当第四个值为0.0时,向量不是“同质的”,因此不会应用转换在正常情况下是理想的,因为正常不是一个职位,而是一个*/vec4 modelNormal = modelMatrix * vec4(normal,0.0); // 为什么设置0.0 就是不希望正常,保持法线向上vPosition = modelPosition.xyz;vNormal = modelNormal.xyz;
}

6.random2D.glsl 随机数方法

float random2D(vec2 value)
{return fract(sin(dot(value.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

二、效果

Hologram Shader


总结

全息影像显示,可以加到自己个人的项目中。感觉还不错!

http://www.lryc.cn/news/502410.html

相关文章:

  • 文件包含进阶玩法以及绕过姿态
  • Markdown编辑器工具--Typora
  • PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语
  • 四十六:如何使用Wireshark解密TLS/SSL报文?
  • 【人工智能】OpenAI O1模型:超越GPT-4的长上下文RAG性能详解与优化指南
  • Ubuntu22.04搭建FTP服务器保姆级教程
  • 操作系统(4)操作系统的结构
  • Python数据分析(OpenCV视频处理)
  • 跨域 Cookie 共享
  • 【视频异常检测】Real-Time Anomaly Detection and Localization in Crowded Scenes 论文阅读
  • 设计模式12:抽象工厂模式
  • 论文学习——多种变化环境下基于多种群进化的动态约束多目标优化
  • Jenkins参数化构建详解(This project is parameterized)
  • Cerebras 推出 CePO,填补推理与规划能力的关键空白
  • 广东省食品销售中高级题库及答案
  • JAVA基础-深入理解Java内存模型(一)-- 重排序与先行发生原则(happens-before)
  • 【Lambda】java之lambda表达式stream流式编程操作集合
  • 家具购物小程序+php
  • 【GIS教程】使用GDAL-Python将tif转为COG并在ArcGIS Js前端加载-附完整代码
  • VB.net进行CAD二次开发(二)与cad交互
  • 【NLP 11、Adam优化器】
  • 51单片机应用开发(进阶)---串口接收字符命令
  • redis 怎么样删除list
  • 【数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
  • npm或yarn包配置地址源
  • STUN服务器用于内网NAT的方案
  • Linux 简单命令总结
  • Vue.js组件开发:提升你的前端工程能力
  • 使用 Pandas 读取 JSON 数据的五种常见结构解析
  • C++鼠标轨迹算法(鼠标轨迹模拟真人移动)