【Three.js基础学习】31.Lights Shading

前言

关于灯光如何在着色器中应用!

下面将创建三个灯光 分别是点光源,环境光,方向光通过这几种光应用着色器显示对应阴影

学习灯光阴影,着色器的使用

    添加三盏灯

    点光,方向光,环境光

    创建一个环境光

    在现实生活中,如果一个物体是完全蓝色的,而唯一的光源是红色的你将看不到这个物体,因为它会吸收光线,不会反射回你的眼睛。

    创建一个方向光

    如果面部面对光线,它将接收全部功率。如果面部处于完美的90°角度,它将不会接收任何功率。中间的值将被插值。

    法线

    我们把面部方向设为正常,把光的方向设为光方向。

    如果它们在同一条直线上,我们希望1

    如果它们处于90°角,我们希望0

    在中间,我们希望插值值

    问题1

    此时方向光在前方,但是随着旋转,光线一直在跟着边,不对的

    因为设置的法线是一直向上的,模型旋转,法线跟着旋转了

    因此我们应该让模型转化应用于法线 (上一节中也有这个问题,和对应解决)

    解决 vec4 modelNormal = modelMatrix * vec4(normal,0.0); // 为什么设置0.0 就是不希望正常,保持法线向上

    修复环境光

    由于环境光也引用,但是在模型背面是黑色的,因为是-1,所以要保证为正数(在正常环境中不可能漆黑)

    法线保持正数

    视图方向 (计算光反射)

    视图的光 = 模型位置 - 相机位置


 

    创建一个点光

    应用衰败

下面是一些应用的思路

环境光对应照到模型上

方向光对应照在模型上,对应反射,和视图反射(镜面)

相机,光源,物体对应获取 光的距离

法线在反射时,对应数值不对,重设

项目结构


一、代码

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 shadingVertexShader from './shaders/shading/vertex.glsl'
import shadingFragmentShader from './shaders/shading/fragment.glsl'
import { DirectionalLightHelper, DoubleSide } from 'three'


/**
 * 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,
    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 = 7
camera.position.y = 7
camera.position.z = 7
scene.add(camera)

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

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
})
// renderer.toneMapping = THREE.ACESFilmicToneMapping
// renderer.toneMappingExposure = 3
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)

/**
 * Material
 */
const materialParameters = {}
materialParameters.color = '#ffffff'

const material = new THREE.ShaderMaterial({
    vertexShader: shadingVertexShader,
    fragmentShader: shadingFragmentShader,
    uniforms:
    {
        uColor: new THREE.Uniform(new THREE.Color(materialParameters.color)),
    }
})

gui
    .addColor(materialParameters, 'color')
    .onChange(() =>
    {
        material.uniforms.uColor.value.set(materialParameters.color)
    })

/**
 * 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.scene
        suzanne.traverse((child) =>
        {
            if(child.isMesh)
                child.material = material
        })
        scene.add(suzanne)
    }
)

/**
 * Light helpers
 */
// directional light helper
const directionalLightHelpter = new THREE.Mesh(
    new THREE.PlaneGeometry(),
    new THREE.MeshBasicMaterial()
)
directionalLightHelpter.material.color.setRGB(0.1,0.1,1.0)
directionalLightHelpter.material.side = THREE.DoubleSide // 侧面
directionalLightHelpter.position.set(0,0,3)
scene.add(directionalLightHelpter)

// Point light helper
const pointLightHelper = new THREE.Mesh(
    new THREE.IcosahedronGeometry(0.1,2),
    new THREE.MeshBasicMaterial()
)
pointLightHelper.material.color.setRGB(1.0,0.1,0.1)
pointLightHelper.position.set(0,2.5,0)
scene.add(pointLightHelper)

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

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

    // Rotate objects
    if(suzanne)
    {
        suzanne.rotation.x = - elapsedTime * 0.1
        suzanne.rotation.y = elapsedTime * 0.2
    }

    sphere.rotation.x = - elapsedTime * 0.1
    sphere.rotation.y = elapsedTime * 0.2

    torusKnot.rotation.x = - elapsedTime * 0.1
    torusKnot.rotation.y = elapsedTime * 0.2

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

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

tick()

index.html

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

style.css

*
{
    margin: 0;
    padding: 0;
}

html,
body
{
    overflow: hidden;
}

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

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
    vec4 modelNormal = modelMatrix * vec4(normal,0.0); //0.0 指的是非齐次向量,指是模型移动,但是法线不移动设置的

    // Varying
    vNormal = modelNormal.xyz; // 应用
    vPosition = modelPosition.xyz;
}

fragment.glsl 片段着色器

uniform vec3 uColor;

varying vec3 vNormal;
varying vec3 vPosition;

/* 
    dot向量x,y之间的点积
    pow(x,y) x的y次方。如果x小于0,结果是未定义的。同样,如果x=0并且y<=0,结果也是未定义的
 */

#include ../includes/ambientLight.glsl
#include ../includes/directionalLight.glsl
#include ../includes/pointLight.glsl

void main()
{
    // 确保法线正常化
    vec3 normal = normalize(vNormal);

    vec3 viewDirection = normalize(vPosition - cameraPosition); // 常量化
    vec3 color = uColor;

    vec3 light = vec3(0.0);
    // 将灯光相加
    light += ambientLight(
        vec3(1.0),
        0.03
    ); 

    light += directionalLight(
        vec3(0.1,0.1,1.0),  // 颜色
        1.0,                // 强度
        normal,            // 法线
        vec3(0.0,0.0,3.0),   // 光的位置
        viewDirection ,     // 视图方向
        20.0                // 镜面反射功率 specular power
    );

    light += pointLight(
        vec3(1.0,0.1,0.1),  // 颜色
        1.0,                // 强度
        normal,            // 法线
        vec3(0.0,2.5,0.0),   // 光的位置
        viewDirection ,     // 视图方向
        20.0 ,               // 镜面反射功率 specular power
        vPosition,            // 位置
        0.3                  // 衰变
    );

    // 最后相乘颜色
    color *= light;

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

ambientLight.glsl

// 创建一个自然光  颜色,强度
vec3 ambientLight(vec3 lightColor, float lightIntensity){ 
    return lightColor * lightIntensity; // 将颜色和光的强度相乘
}

directionalLight.glsl


// 创建一个方向光  颜色,强度 , 法线 , 光位置 ,视图位置 ,镜面反射率
vec3 directionalLight(vec3 lightColor, float lightIntensity,vec3 normal ,vec3 lightPosition, vec3 viewDirection, float specularPower){ 
    
    vec3 lightDirection = normalize(lightPosition); //将vector向量 转换成单位向量  长度为1
    vec3 lightReflection = reflect(- lightDirection,normal);

    // shading 
    float shading = dot(normal,lightDirection);
    shading = max(0.0, shading); // 保证永远不会低于0.0

    // Specular  光反射和观看方向的一个点 得到反射 由于点积是相反,所以取负值
    float specular = - dot(lightReflection, viewDirection);
    // 保证我们的点积不是负数,因为在背面不需要有反射
    specular = max(0.0,specular);
    specular = pow(specular,specularPower);
    
        return lightColor * lightIntensity * (shading +  specular); // 将颜色和光的强度相乘
    // return vec3(specular); // 镜面
    // return vec3(shading); // 阴影
}

pointLight.glsl

// 创建一个点光  
vec3 pointLight(vec3 lightColor, float lightIntensity,vec3 normal ,vec3 lightPosition, vec3 viewDirection, float specularPower, vec3 position,float lightDecay){ 
    
    vec3 lightDelta = lightPosition - position; // 为什么相减,原因获得点光的位置
    float lightDistance = length(lightDelta); // 想要向量的长度 length 点光到物体表面的距离
    vec3 lightDirection = normalize(lightDelta); //将vector向量 转换成单位向量  长度为1
    vec3 lightReflection = reflect(- lightDirection,normal);

    // shading 
    float shading = dot(normal,lightDirection);
    shading = max(0.0, shading); // 保证永远不会低于0.0

    // Specular  光反射和观看方向的一个点 得到反射 由于点积是相反,所以取负值
    float specular = - dot(lightReflection, viewDirection);
    // 保证我们的点积不是负数,因为在背面不需要有反射
    specular = max(0.0,specular);
    specular = pow(specular,specularPower);

    // Decay
    float decay = 1.0 - lightDistance * lightDecay;
    decay = max(0.0,decay);

    return lightColor * lightIntensity * decay *(shading +  specular); // 将颜色和光的强度相乘
}

效果

光源-着色器应用阴影显示


总结

先看,然后知道功能应用场景,自己想要实现时候,会有参照的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值