一.从一个shader开始
Three.js已经简化了很多写shader的工作,最主要的工作就是他把一些常用的矩阵已经作为一个常量提供给我们。
首先来看
Three.js官网
的说明,一些常用参数已经和几何元素、相机等关联在一起,我们只需要拿来用即可。
来看第一个shader,通过shader改变几何体表面的颜色,效果还是很炫酷的。
<!DOCTYPE html>
<meta charset="UTF-8">
<title>Three框架</title>
<script src="../library/three.js"></script>
<script src="../library/stats.min.js"></script>
<style type="text/css">
div#canvas-frame {
border: none;
cursor: pointer;
width: 100%;
height: 600px;
background-color: #EEEEEE;
</style>
<script id="fragment_shader" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
void main( void ) {
vec2 position = - 1.0 + 2.0 * vUv;
float red = abs( sin( position.x * position.y + time / 5.0 ) );
float green = abs( sin( position.x * position.y + time / 4.0 ) );
float blue = abs( sin( position.x * position.y + time / 3.0 ) );
gl_FragColor = vec4( red, green, blue, 1.0 );
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main()
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
</script>
<script>
var renderer;
var stats;
var clock;
var uniforms1;
var camera;
var scene;
var light;
var mesh;
//初始化webgl
function initThree() {
width = document.getElementById('canvas-frame').clientWidth;
height = document.getElementById('canvas-frame').clientHeight;
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.getElementById('canvas-frame').appendChild(renderer.domElement);
renderer.setClearColor(0xFFFFFF, 1.0);
clock = new THREE.Clock();
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById('canvas-frame').appendChild(stats.domElement);
//设置相机
function initCamera() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
camera.position.set(0,0,600);
camera.up = new THREE.Vector3(0,1,0);
camera.lookAt(0,0,0);
//初始化场景
function initScene() {
scene = new THREE.Scene();
//设置化灯光
function initLight() {
light = new THREE.AmbientLight(0xFF0000);
light.position.set(100, 100, 200);
scene.add(light);
//几何物体
function initObject() {
uniforms1 = {
time: { value: 1.0 }
var params = uniforms1;
var geometry = new THREE.CylinderGeometry(100, 150, 400);
var material = new THREE.ShaderMaterial({
uniforms: params,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragment_shader').textContent
mesh = new THREE.Mesh(geometry, material);
mesh.position = new THREE.Vector3(0, 0, 0);
scene.add(mesh);
//运行webgl
function threeStart() {
initThree();
initCamera();
initScene();
initLight();
initObject();
animation();
//设置动态场景
function animation() {
var delta = clock.getDelta();
uniforms1.time.value += delta * 5;
renderer.render(scene, camera);
requestAnimationFrame(animation);
stats.update();
</script>
</head>
<body οnlοad="threeStart();">
<div id="canvas-frame"></div>
</body>
</html>
然后再来分析代码,看是如何实现的。
1)先看vertexshader,这个简单明了,gl_Position=mvp,然后将UV坐标传递到fragmentshader中,在fragmentshader中定义一个time参数,然后点的颜色随着时间和位置的变化而变化。
2)接下来在创建几何物体的时候,取到time这个参数,几何体中的材料采用shaderMaterial();
3)最后在animation里随着时间改变time这个参数。
二.简化shader
在片面着色器中,如果y坐标大于0,那么颜色为红色,如果y坐标小于0,颜色为绿色。我们只需要在上述基础上修改下片元着色器,然后从顶点传进来一个坐标pos。
<script id="fragment_shader" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
varying vec4 pos;
void main( void ) {
if(pos.y>0.0){
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
else{
gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
varying vec4 pos;
void main()
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
pos=projectionMatrix * mvPosition;
</script>
最后结果:
三、进一步完善效果,这个时候发现两个颜色界限太明显,如果能够柔和过渡一下效果可能会更好一些。
根据这种想法,我们把过渡段的正负100的位置加上条件判断,从而确定过渡段的颜色。这里的一个关键点是用了mix这个内置函数。
<script id="fragment_shader" type="x-shader/x-fragment">
uniform float time;
varying vec2 vUv;
varying vec4 pos;
void main( void ) {
if(pos.y>100.0){
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
else if(pos.y<-100.0){
gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );
else{
gl_FragColor = mix(vec4( 0.0, 1.0, 0.0, 1.0 ),vec4( 1.0, 0.0, 0.0, 1.0 ),smoothstep(-100.0,100.0,pos.y));
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
varying vec4 pos;
void main()
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
pos=projectionMatrix * mvPosition;
</script>
最终效果:
Three.js已经简化了很多写shader的工作,最主要的工作就是他把一些常用的矩阵已经作为一个常量提供给我们。 首先来看Three.js官网的说明,一些常用参数已经和几何元素、相机等关联在一起,我们只需要拿来用即可。 来看第一个shader,通过shader改变几何体表面的颜色,效果还是很炫酷的。&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;...
下面是
shader
代码,在
webgl
可以运行,在three也可以
<script id="vertex
shader
" type="x-
shader
/x-vertex">
attribute vec3 A_position3;
attribute vec2 A_uv;
uniform mat4 mvp;
varying vec2 out_uv;
void main(void){
webgl
智慧楼宇发光效果算法系列之高斯模糊
如果使用过PS之类的图像处理软件,相信对于模糊滤镜不会陌生,图像处理软件提供了众多的模糊算法。高斯模糊是其中的一种。
在我们的智慧楼宇的项目中,要求对楼宇实现楼宇发光的效果。 比如如下图所示的
简单
楼宇效果:
楼宇发光效果需要用的算法之一就是高斯模糊。
高斯模糊简介
高斯模糊算法是计算机图形学领域中一种使用广泛的技术, 是一种图像空间效果,用于对图像进行模糊处理,创建原始图像的柔和模糊版本。
使用高斯模糊的效果,结合一些其他的算法,还可以产生发光,光晕,景深,热
import Refractor from 'three/examples/js/objects/Refractor';
import WaterRefraction
Shader
from 'three/examples/js/
shader
s/WaterRefraction
Shader
';
let refractorGeometry = new THREE.PlaneBuf...
在前几节中我们分别学习 点 线 三角形的学习。今天我们给三角形添加一个渐变色
在这个章节中如果你跟着看了前几张的学习内容,你就很快可以理解本章的内容了。如果你并没有
webgl
的经验,你可以去我的博文中看我之前的文章;
这一章节中其实与上创建线段与三角形并没有上面区别,唯独有两个点需要注意一下;
第一需要注意的地方,在于glsl语言定义中我们需要把顶点着色器与片元着色器结合起来,请看下面的...
## 1. 管道的创建
在
three.js
中,可以通过THREE.CylinderGeometry类创建一个圆柱体,然后通过THREE.Mesh类将其转换为网格对象,最后添加到场景中。代码如下:
```javascript
// 创建圆柱体
var geometry = new THREE.CylinderGeometry(1, 1, 10, 32);
// 创建材质
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
// 创建网格对象
var cylinder = new THREE.Mesh(geometry, material);
// 添加到场景中
scene.add(cylinder);
其中,CylinderGeometry类的构造函数参数依次为:顶部半径、底部半径、高度、分段数。
## 2. 管道的弯曲
如果需要创建弯曲的管道,可以使用THREE.TubeGeometry类。该类需要传入一个路径曲线对象,表示管道的路径。代码如下:
```javascript
// 创建路径曲线
var curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, 0, 0),
new THREE.Vector3(-5, 5, 0),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, -5, 0),
new THREE.Vector3(10, 0, 0)
// 创建管道
var geometry = new THREE.TubeGeometry(curve, 64, 1, 8, false);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var tube = new THREE.Mesh(geometry, material);
scene.add(tube);
其中,TubeGeometry类的构造函数参数依次为:路径曲线对象、分段数、管道半径、分段数、是否闭合。
## 3. 管道的纹理
要给管道添加纹理,可以使用THREE.TextureLoader类加载纹理图片,并将其传入到材质对象中。代码如下:
```javascript
// 加载纹理
var texture = new THREE.TextureLoader().load('textures/metal.jpg');
// 创建材质
var material = new THREE.MeshBasicMaterial({map: texture});
// 创建管道
var geometry = new THREE.CylinderGeometry(1, 1, 10, 32);
var cylinder = new THREE.Mesh(geometry, material);
scene.add(cylinder);
其中,TextureLoader类的load方法可以加载图片,并返回一个纹理对象。MeshBasicMaterial类的map属性可以将纹理对象传入到材质中。
## 4. 管道的动画
通过改变管道的位置、旋转等属性,可以实现管道的动画效果。例如,可以使用Tween.js库实现管道的平移动画。代码如下:
```javascript
// 创建管道
var geometry = new THREE.CylinderGeometry(1, 1, 10, 32);
var material = new THREE.MeshBasicMaterial({color: 0xff0000});
var cylinder = new THREE.Mesh(geometry, material);
scene.add(cylinder);
// 创建动画
var tween = new TWEEN.Tween(cylinder.position)
.to({x: 10}, 1000)
.easing(TWEEN.Easing.Quadratic.Out)
.repeat(Infinity)
.yoyo(true)
.start();
// 更新动画
function animate() {
requestAnimationFrame(animate);
TWEEN.update();
renderer.render(scene, camera);
animate();
其中,使用Tween.js库创建一个Tween对象,并设置管道的位置属性为动画的目标属性。然后通过调用start方法启动动画。使用requestAnimationFrame方法更新动画。在每一帧中调用TWEEN.update方法更新Tween对象的属性。