蒸汽波太阳
实现效果
你们的点赞、评论是我不断向前的动力
思路
绘制太阳 太阳的颜色 黄到红 绘制线 高到低逐渐变厚
首先创建基础页面内容,使用2*2大小的平面放置我们的太阳
内容中对之前提到的基础内容可以查看往期文章
1、three场景代码
vue页面代码
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import vertexShader from './main.vert'
import fragmentShader from './main.frag'
onMounted(() => {})
let scene = new THREE.Scene()
const renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
})
renderer.setClearColor(0xffffff)
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.z = 5
// 添加相机控制器
new OrbitControls(camera, renderer!.domElement)
const sunPlane = new THREE.PlaneGeometry(2, 2)
const material = new THREE.ShaderMaterial({
fragmentShader: fragmentShader,
vertexShader: vertexShader,
uniforms: {
uTime: { value: 0 },
},
side: THREE.DoubleSide,
})
const sunMesh = new THREE.Mesh(sunPlane, material)
scene.add(sunMesh)
const render = (): void => {
renderer.render(scene, camera)
}
const clock = new THREE.Clock()
const animate = (): void => {
requestAnimationFrame(animate)
material.uniforms.uTime.value = clock.getElapsedTime()
render()
}
animate()
onBeforeUnmount(() => {
// destroyThree() // TODO: 后面再补
document.body.removeChild(renderer.domElement) // 避免热更新时创建新的canvas
})
</script>
<template>
<div class="demo"></div>
</template>
<style scoped></style>
顶点着色器代码
uniform float uTime;
varying vec2 vUv;
void main(){
vec4 mPosition=modelMatrix*vec4(position,1.);
gl_Position=projectionMatrix*viewMatrix*mPosition;
vUv = uv;
}
片段着色器代码
varying vec2 vUv;
uniform float uTime;
void main(){
vec3 color=vec3(0.);
gl_FragColor=vec4(color,1.);
}
得到一块黑色平面
本文主要修改内容为片段着色器 所以只关注这块就好
2、绘制太阳
计算利用 length 与 smoothstep 绘制太阳的形状
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular = smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
color += circular;
gl_FragColor=vec4(color,1.);
}
利用mix 混合两种颜色创建太阳颜色
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(1.,0.,.2),vec3(1.,1.0,0.),uv.y);
color+=circular*sumCol;
gl_FragColor=vec4(color,1.);
}
此时太阳边缘 绘制时留下的过渡效果 让边缘看着很模糊 增加太阳颜色中 红色与绿色
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
3、绘制线
利用fract取小数吧uv分20份
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
float line=fract(uv.y*20.);
// color+=circular*sumCol;
color+=line;
gl_FragColor=vec4(color,1.);
}
结果 减去 0.5 取绝对值
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
float line=fract(uv.y*20.);
line = abs(line-.5);
// color+=circular*sumCol;
color+=line;
gl_FragColor=vec4(color,1.);
利用step函数 生成没有过渡效果的 黑白条纹
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
float line=fract(uv.y*20.);
line = abs(line-.5);
line = step(line,.2);
// color+=circular*sumCol;
color+=line;
gl_FragColor=vec4(color,1.);
目前效果和我们预期中由窄到宽不一致 可以通过增加一个渐变值来处理
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
// 递减值
float decreasing = (-uv.y + 0.3);
float line=fract(uv.y*20.);
line = abs(line-.5);
line = step(line,.2);
// color+=circular*sumCol;
color+=decreasing;
gl_FragColor=vec4(color,1.);
}
减去递减值 得到由上到下 增厚的线
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
// 递减值
float decreasing=(-uv.y+.3);
float line=fract(uv.y*20.);
line=abs(line-.5);
line-=decreasing;
line=step(line,.2);
// color+=circular*sumCol;
color+=line;
gl_FragColor=vec4(color,1.);
}
通过与前面的太阳叠加 叠加前通过 1.0 -line 把线图案的黑白位置替换
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
// 递减值
float decreasing=(-uv.y+.3);
float line=fract(uv.y*20.);
line=abs(line-.5);
line-=decreasing;
line=step(line,.2);
color+=circular*sumCol*(1.-line);
gl_FragColor=vec4(color,1.);
}
最后让太阳线动起来
varying vec2 vUv;
uniform float uTime;
void main(){
vec2 uv=vUv;
float circular=length(uv-vec2(.5));
circular=smoothstep(.3,.29,circular);
vec3 color=vec3(0.);
vec3 sumCol=mix(vec3(4.,0.,.2),vec3(1.,1.1,0.),uv.y);
// 递减值
float decreasing=(-uv.y+.3);
uv.y +=uTime/10.;
float line=fract(uv.y*20.);
line=abs(line-.5);
line-=decreasing;
line=step(line,.2);
color+=circular*sumCol*(1.-line);
gl_FragColor=vec4(color,1.);
}