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

【Three.js基础学习】34.Earch Shaders

前言

回顾:

        黑暗面城市的照明

       

        太阳的反光主要在海洋上可见白天和黑夜之间的部分

        (黄昏)看起来是红色的

        大气在地球周围产生光辉(就像一个体积)

        我们不追求基于物理效果的渲染,这不会阻止最终结果看起来良好且逼真。

        细分球体缓慢旋转

        src/shaders/earth/中的基础着色器

        lil-gui

        vite-plugin-glsl

        轨道控制

        纹理加载器

        在颜色纹理上更改颜色空间

        earthDayTexture.colorSpace = THREE.SRGBColorSpace

        问题

        混合白天 黑天 与太阳位置相比 的颜色?  mix

        我们将使用通常的点状产品,但我们需要一个光的方向。目前,我们将在GLSL中创建太阳方向,稍后我们将使用一个统一的系统来控制

        updateSun()

        云层

        一种做法是在地球上方的一个球体上添加云层,这使得云层具有一些灵活性,例如可以独立旋转云层。将整个云层旋转看起来不好,我们需要稍微增大球体的大小以防止Z轴冲突。

        真实环境下 球体在白天 黑夜和中间得过渡  应该有颜色变化

        还可以添加功能

        添加更多调整

        测试与其他行星纹理

        按照现实中地球相对于太阳的自转来使地球转动通过在UV上添加一些位移来动画化云使用Perin函数或Perlin纹理创建云为太阳添加Lensflare(示例)(在静态/镜头中提供了纹理)

        在后面添加星星或银河系环境图

项目结构


一、代码

three.js

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import GUI from 'lil-gui'
import earthVertexShader from './shaders/earth/vertex.glsl'
import earthFragmentShader from './shaders/earth/fragment.glsl'
import atmosphereVertexShader from './shaders/atmosphere/vertex.glsl'
import atmosphereFragmentShader from './shaders/atmosphere/fragment.glsl'

/**
 * Base
 */
// Debug
const gui = new GUI()

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

// Loaders
const textureLoader = new THREE.TextureLoader()

/**
 * Earth 地球
 */
const earthParameters = {}
earthParameters.atmosphereDayColor = '#00aaff' // 白天颜色 大气层
earthParameters.atmosphereTwilightColor = '#ff6600' // 傍晚

gui
    .addColor(earthParameters,'atmosphereDayColor')
    .onChange(()=>{
        earthMaterial.uniforms.uAtmosphereDayColor.value.set(earthParameters.atmosphereDayColor) // 设置颜色
        atmosphereMaterial.uniforms.uAtmosphereDayColor.value.set(earthParameters.atmosphereDayColor) // 设置颜色
    })

gui
    .addColor(earthParameters,'atmosphereTwilightColor')
    .onChange(()=>{
        earthMaterial.uniforms.uAtmosphereTwilightColor.value.set(earthParameters.atmosphereTwilightColor) // 设置颜色
        atmosphereMaterial.uniforms.uAtmosphereTwilightColor.value.set(earthParameters.atmosphereTwilightColor) // 设置颜色
    })

// texture
const earthDayTexture = textureLoader.load('./earth/day.jpg')
earthDayTexture.colorSpace = THREE.SRGBColorSpace
earthDayTexture.anisotropy = 8  // 边缘处 防止有割裂

const earthNightTexture = textureLoader.load('./earth/night.jpg')
earthNightTexture.colorSpace = THREE.SRGBColorSpace
earthNightTexture.anisotropy = 8

const earthSpecularCloudsTexture = textureLoader.load('./earth/specularClouds.jpg')
earthSpecularCloudsTexture.anisotropy = 8

// Mesh
const earthGeometry = new THREE.SphereGeometry(2, 64, 64)
const earthMaterial = new THREE.ShaderMaterial({
    vertexShader: earthVertexShader,
    fragmentShader: earthFragmentShader,
    uniforms:
    {
        uDayTexture:new THREE.Uniform(earthDayTexture),
        uNightTexture:new THREE.Uniform(earthNightTexture),
        uSpecularCloudsTexture:new THREE.Uniform(earthSpecularCloudsTexture),
        uSunDirection:new THREE.Uniform(new THREE.Vector3(0,0,1)),
        uAtmosphereDayColor: new THREE.Uniform(new THREE.Color(earthParameters.atmosphereDayColor)),
        uAtmosphereTwilightColor: new THREE.Uniform(new THREE.Color(earthParameters.atmosphereTwilightColor)),
    }
})
const earth = new THREE.Mesh(earthGeometry, earthMaterial)
scene.add(earth)

// Atmosphere 效果大气层周围光晕
const atmosphereMaterial = new THREE.ShaderMaterial(
    {
        vertexShader:atmosphereVertexShader,
        fragmentShader:atmosphereFragmentShader,
        uniforms:
        {
            uSunDirection:new THREE.Uniform(new THREE.Vector3(0,0,1)),
            uAtmosphereDayColor: new THREE.Uniform(new THREE.Color(earthParameters.atmosphereDayColor)),
            uAtmosphereTwilightColor: new THREE.Uniform(new THREE.Color(earthParameters.atmosphereTwilightColor)),
        },
        side:THREE.BackSide, // 只展示后面
        transparent:true
    }
)
const atmosphere = new THREE.Mesh(earthGeometry,atmosphereMaterial)
atmosphere.scale.set(1.04,1.04,1.04)
scene.add(atmosphere)

/* 
    sun
*/
const sunSpherical = new THREE.Spherical(1,Math.PI * 0.5 * 0.5) // 创建几何体
const sunDirection = new THREE.Vector3()

// Debug
const debugSun = new THREE.Mesh(
    new THREE.IcosahedronGeometry(0.1,2),
    new THREE.MeshBasicMaterial()
)
scene.add(debugSun)

// Update
const updateSun = () =>{
    // Sun direction
    sunDirection.setFromSpherical(sunSpherical)

    // Debug
    debugSun.position.copy(sunDirection).multiplyScalar(5) // 设置位置

    // Uniforms
    earthMaterial.uniforms.uSunDirection.value.copy(sunDirection);
    atmosphereMaterial.uniforms.uSunDirection.value.copy(sunDirection);
}
updateSun()

// Tweaks
gui 
    .add(sunSpherical,'phi')
    .min(0)
    .max(Math.PI)
    .onChange(updateSun)

gui 
    .add(sunSpherical,'theta')
    .min(-Math.PI)
    .max(Math.PI)
    .onChange(updateSun)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight,
    pixelRatio: Math.min(window.devicePixelRatio, 2)
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight
    sizes.pixelRatio = Math.min(window.devicePixelRatio, 2)

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(sizes.pixelRatio)
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(25, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 12
camera.position.y = 5
camera.position.z = 4
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)
renderer.setClearColor('#000011')

// console.log(renderer.antialias)

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    earth.rotation.y = elapsedTime * 0.1

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

style.css

*
{
    margin: 0;
    padding: 0;
}

html,
body
{
    overflow: hidden;
}

.webgl
{
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}

index.html

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

earth/fragment.glsl

uniform sampler2D uDayTexture; // texture2D 2D采样器
uniform sampler2D uNightTexture; 
uniform sampler2D uSpecularCloudsTexture; 
uniform vec3 uSunDirection;
uniform vec3 uAtmosphereDayColor;
uniform vec3 uAtmosphereTwilightColor;

varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;

/* 
    dot向量x,y之间的点积
    mix(x, y, a)返回线性混合的x和y,如:x*(1−a)+y*a
    smoothstep(edge0, edge1, x)
    如果x <= edge0,返回0.0 ;如果x >= edge1 返回1.0;如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。如果edge0 >= edge1,结果是未定义的。
    pow(x,y) x的y次方。如果x小于0,结果是未定义的。同样,如果x=0并且y<=0,结果也是未定义的。
 */ 

void main()
{
    vec3 viewDirection = normalize(vPosition - cameraPosition);
    vec3 normal = normalize(vNormal);
    vec3 color = vec3(0.0);

    // Sun orientation
    float sunOrientation = dot(uSunDirection,normal);

    // Day / night color
    float dayMix = smoothstep(-0.25,0.5,sunOrientation);  // 平滑过渡 限制值
    vec3 dayColor = texture2D(uDayTexture,vUv).rgb;
    vec3 nightColor = texture2D(uNightTexture,vUv).rgb;
    color = mix(nightColor,dayColor,dayMix);

    // Specular clouds color
    vec2 specularCloudsColor = texture2D(uSpecularCloudsTexture,vUv).rg;

    // Clouds
    float cloudsMix = smoothstep(0.5,1.0,specularCloudsColor.g); // 改变云得多少
    cloudsMix *= dayMix;  // 晚上不想要云,相乘这样到了晚上云就会消失
    color = mix(color,vec3(1.0),cloudsMix); // 将云和原本得混合起来

    // Fresnel 菲涅尔效果
    // Fresnel 根据相机角度,进行判断,相同方向1,直角0 ,相反 1
    // 同样法线应该渲染,vertex.glsl
    float fresnel = dot(viewDirection,normal) + 1.0;
    fresnel = pow(fresnel,2.0);

    // Atmosphere 得到大气
    float almosphereDayMix = smoothstep(-0.5,1.0,sunOrientation); // 白天
    vec3 atmosphereColor = mix(uAtmosphereTwilightColor,uAtmosphereDayColor,almosphereDayMix);
    color = mix(color,atmosphereColor,fresnel * almosphereDayMix); // 相乘会得到在黄昏只是轻微的

    // Speculare  太阳的反射光 根据光线计算反射
    vec3 reflection = reflect(- uSunDirection , normal);  // 镜面折射
    float specular = - dot(reflection , viewDirection); // 倒影,所以取负数
    specular = max(specular, 0.0); // 设置界限值
    specular = pow(specular, 32.0); // 设置值的大小
    // 大陆和海洋的反射光不应一样 ,specularCloudsColor.r
    specular *= specularCloudsColor.r;

    // 从后面看 白色光太强 应减少 ,应该和大气层结合 在特定角度 改变成大气的颜色
    // 混合大气颜色,菲涅尔效果
    vec3 specularColor = mix(vec3(1.0),atmosphereColor,fresnel);
    // 和 反射光相乘 就可以得到根据 大气颜色反射光颜色改变
    color += specular * specularColor;

    // Final color
    gl_FragColor = vec4(color, 1.0);
    #include <tonemapping_fragment>
    #include <colorspace_fragment>
}

earth/vertex.glsl

varying vec2 vUv;
varying vec3 vNormal;
varying vec3 vPosition;

void main()
{
    // Position
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    gl_Position = projectionMatrix * viewMatrix * modelPosition;

    // Model normal
    vec3 modelNormal = (modelMatrix * vec4(normal, 0.0)).xyz;

    // Varyings
    vUv = uv;
    vNormal = modelNormal;
    vPosition = modelPosition.xyz;
}

atmosphere/fragment.glsl


uniform vec3 uSunDirection;
uniform vec3 uAtmosphereDayColor;
uniform vec3 uAtmosphereTwilightColor;


varying vec3 vNormal;
varying vec3 vPosition;

/* 
    dot向量x,y之间的点积
    mix(x, y, a)返回线性混合的x和y,如:x*(1−a)+y*a
    smoothstep(edge0, edge1, x)
    如果x <= edge0,返回0.0 ;如果x >= edge1 返回1.0;如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。如果edge0 >= edge1,结果是未定义的。
    pow(x,y) x的y次方。如果x小于0,结果是未定义的。同样,如果x=0并且y<=0,结果也是未定义的。
 */ 

void main()
{
    vec3 viewDirection = normalize(vPosition - cameraPosition);
    vec3 normal = normalize(vNormal);
    vec3 color = vec3(0.0);

    // Sun orientation
    float sunOrientation = dot(uSunDirection,normal);

    // Atmosphere 得到大气
    float almosphereDayMix = smoothstep(-0.5,1.0,sunOrientation); // 白天
    vec3 atmosphereColor = mix(uAtmosphereTwilightColor,uAtmosphereDayColor,almosphereDayMix);
    color =atmosphereColor; // 相乘会得到在黄昏只是轻微的

    // Alpha
    float edgeAlpha = dot(viewDirection,normal);
    edgeAlpha = smoothstep(0.0,0.5,edgeAlpha);
    
    float dayAlpha = smoothstep(-0.5, 0.0 ,sunOrientation);

    float alpha = edgeAlpha * dayAlpha;

    // Final color
    gl_FragColor = vec4(color, alpha);
    #include <tonemapping_fragment>
    #include <colorspace_fragment>
}

atmosphere/vertex.glsl


varying vec3 vNormal;
varying vec3 vPosition;

void main()
{
    // Position
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    gl_Position = projectionMatrix * viewMatrix * modelPosition;

    // Model normal
    vec3 modelNormal = (modelMatrix * vec4(normal, 0.0)).xyz;

    // Varyings
    vNormal = modelNormal;
    vPosition = modelPosition.xyz;
}

二、效果

地球着色器


总结

关于地球着色器的实现!


http://www.kler.cn/a/481811.html

相关文章:

  • 分布式环境下定时任务扫描时间段模板创建可预订时间段
  • Elixir语言的学习路线
  • Sql 创建用户
  • (概率论)无偏估计
  • 【网络协议】静态路由详解
  • leetcode 5. 最长回文子串
  • Redis 管道技术(Pipeline)
  • 2025新春烟花代码(二)HTML5实现孔明灯和烟花效果
  • 源代码防泄漏一机两用合体方案
  • 芯片详细讲解,从而区分CPU、MPU、DSP、GPU、FPGA、MCU、SOC、ECU
  • 数据结构:LinkedList与链表—无头单向链表(一)
  • 解决OPenMP不能使用头文件#include <omp.h> 的问题
  • SQLite PRAGMA
  • LQ quarter 5th
  • 缓存-Redis-缓存更新策略-主动更新策略-Cache Aside Pattern(全面 易理解)
  • OpenAI 宣称已掌握构建通用人工智能 (AGI) 的方法| 0107AI日报
  • Docker--Docker Volume(存储卷)
  • 【Java 注解】从入门到精通:上篇
  • 安装完docker后,如何拉取ubuntu镜像并创建容器?
  • 机器学习无处不在,AI顺势而为,创新未来
  • OpenAI开源的多智能体框架Swarm
  • 农业信息化、智慧农业领域工作实践总结以及展望
  • 国产操作系统两款图像处理软件
  • JetBrains IDEs和Visual Studio Code的对比
  • Docker中安装Tailscale方法三
  • springboot项目读取resources目录下文件