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

ops/2024/12/19 12:25:38/

前言

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

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

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

    添加三盏灯

    点光,方向光,环境光

    创建一个环境光

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

    创建一个方向光

    如果面部面对光线,它将接收全部功率。如果面部处于完美的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 sizessizes.width = window.innerWidthsizes.height = window.innerHeightsizes.pixelRatio = Math.min(window.devicePixelRatio, 2)// Update cameracamera.aspect = sizes.width / sizes.heightcamera.updateProjectionMatrix()// Update rendererrenderer.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.scenesuzanne.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 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()

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()
{// Positionvec4 modelPosition = modelMatrix * vec4(position, 1.0);gl_Position = projectionMatrix * viewMatrix * modelPosition;// Model normalvec4 modelNormal = modelMatrix * vec4(normal,0.0); //0.0 指的是非齐次向量,指是模型移动,但是法线不移动设置的// VaryingvNormal = 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.glslvoid 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 powervPosition,            // 位置0.3                  // 衰变);// 最后相乘颜色color *= light;// Final colorgl_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向量 转换成单位向量  长度为1vec3 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向量 转换成单位向量  长度为1vec3 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);// Decayfloat decay = 1.0 - lightDistance * lightDecay;decay = max(0.0,decay);return lightColor * lightIntensity * decay *(shading +  specular); // 将颜色和光的强度相乘
}

效果

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


总结

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


http://www.ppmy.cn/ops/143178.html

相关文章

Chinese-Clip实现以文搜图和以图搜图(transformers版)

本文不生产技术&#xff0c;只做技术的搬运工&#xff01; 前言 作者昨天使用cn_clip库实现了一版&#xff0c;但是觉得大家复现配置环境可能有点复杂&#xff0c;因此有使用transformers库实现了一版&#xff0c;提供大家选择&#xff0c;第一篇参考链接如下&#xff1a; Ch…

OpenCV--图像拼接

OpenCV--图像拼接 代码和笔记 代码和笔记 import cv2 import numpy as np""" 图像拼接&#xff1a; 1. 读取图片&#xff0c;灰度化 2. 计算各自的特征点和描述子 3. 匹配特征 4. 计算单应性矩阵 5. 透视变换 6. 创建一个大图&#xff0c;放图两张图 "&qu…

基于yolov10的遥感影像目标检测系统,支持图像检测,视频检测和实时摄像检测功能(pytorch框架,python源码)

更多目标检测、图像分类识别、目标检测等其他项目可看我主页其他文章 功能演示&#xff1a; 基于yolov10的遥感影像目标检测系统&#xff0c;既支持图像检测&#xff0c;也支持视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili &#xff08;一&#xff09;…

AtomGit 开源生态应用开发赛报名开始啦

目录 1、赛项背景2、赛项信息3、报名链接4、赛题一&#xff1a;开发者原创声明&#xff08;DCO&#xff09;应用开发赛题要求目标核心功能 5、赛题二&#xff1a;基于 OpenHarmony 的开源社区应用开发简介赛题要求 6、参赛作品提交初赛阶段决赛阶段 7、参赛作品提交方式 1、赛项…

Linux——Shell

if 语句 格式&#xff1a;if list; then list; [ elif list; then list; ] ... [ else list; ] fi 单分支 if 条件表达式; then 命令 fi 示例&#xff1a; #!/bin/bash N10 if [ $N -gt 5 ]; then echo yes fi # bash test.sh yes 双分支 if 条件表达式; then 命令 else 命令…

Linux安装部署Redis(超级详细)

前言 网上搜索了一筐如何在Linux下安装部署Redis的文章&#xff0c;各种文章混搭在一起勉强安装成功了。自己也记录下&#xff0c;方便后续安装时候有个借鉴之处。 Redis版本 5.0.4服务器版本 Linux CentOS 7.6 64位 下载Redis 进入官网找到下载地址 https://redis.io/down…

爬虫抓取的数据如何有效存储和管理?

在现代数据驱动的世界中&#xff0c;爬虫技术已成为获取网络数据的重要手段。然而&#xff0c;如何有效地存储和管理这些数据是一个关键问题。本文将详细介绍几种有效的数据存储和管理方法&#xff0c;并提供相应的Java代码示例。 1. 数据存储方式 1.1 文件存储 文件存储是最…

access数据库代做/mysql代做/Sql server数据库代做辅导设计服务

针对Access数据库、MySQL以及SQL Server数据库的代做和辅导设计服务&#xff0c;以下是一些关键信息和建议&#xff1a; 一、服务概述 这些服务通常包括数据库的设计、创建、优化、维护以及相关的编程和查询编写等。无论是Access这样的桌面关系数据库管理系统&#xff08;RDB…