【Three.js基础学习】33.Halftone Shading shaders
前言
半色调是一种用于印刷的技术,
通过使用相同颜色但大小不同
的点网格来创建看起来像渐变
色的颜色变化。从远处看,各
种半色调的组合会产生令人愉
悦的色调。
我们将重现这种效果,为我们的3D物体添加阴影和反射。
基本着色器已经在:src/shaders/haftone/中
在包括,文件夹中使用前几节课的轻型函数
111-gu1 with elearColor and ucolor
vite-plugin-glsl
OrbitControls
开始
light 假如环境光 ,方向光
实现网格
gl_FragCoord是一个vec4,其中xy构成“屏幕”坐标,zw用于深度
我们不在乎曼后两个
注意当我观察颜色时,都是白色 ,要除以 分辨率(uResolution)
项目目录
实现思路示意图,跟之前学阴影 展示对应形状差不多,关键步骤代码有注释
一、代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Halftone Shading</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<canvas class="webgl"></canvas>
<script type="module" src="./script.js"></script>
</body>
</html>
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 halftoneVertexShader from './shaders/halftone/vertex.glsl'
import halftoneFragmentShader from './shaders/halftone/fragment.glsl'
/*
半色调是一种用于印刷的技术,
通过使用相同颜色但大小不同
的点网格来创建看起来像渐变
色的颜色变化。从远处看,各
种半色调的组合会产生令人愉
悦的色调。
我们将重现这种效果,为我们的3D物体添加阴影和反射。
基本着色器已经在:src/shaders/haftone/中
在包括,文件夹中使用前几节课的轻型函数
111-gu1 with elearColor and ucolor
vite-plugin-glsl
OrbitControls
开始
light 假如环境光 ,方向光
实现网格
gl_FragCoord是一个vec4,其中xy构成“屏幕”坐标,zw用于深度
我们不在乎曼后两个
注意当我观察颜色时,都是白色 ,要除以 分辨率(uResolution)
*/
/**
* 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 materials
// 更新,当用户调整窗口大小时候,将分辨率发送到着色器,更新
material.uniforms.uResolution.value.set(sizes.width * sizes.pixelRatio,sizes.height * sizes.pixelRatio)
// 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 rendererParameters = {}
rendererParameters.clearColor = '#26132f'
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true
})
renderer.setClearColor(rendererParameters.clearColor)
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(sizes.pixelRatio)
gui
.addColor(rendererParameters, 'clearColor')
.onChange(() =>
{
renderer.setClearColor(rendererParameters.clearColor)
})
/**
* Material
*/
const materialParameters = {}
materialParameters.color = '#ff794d'
materialParameters.shadowColor = '#8e19b8'
materialParameters.lightColor = '#e5ffe0'
const material = new THREE.ShaderMaterial({
vertexShader: halftoneVertexShader,
fragmentShader: halftoneFragmentShader,
uniforms:
{
uColor: new THREE.Uniform(new THREE.Color(materialParameters.color)),
uShadeColor: new THREE.Uniform(new THREE.Color(materialParameters.shadowColor)),
uResolution: new THREE.Uniform(new THREE.Vector2(sizes.width * sizes.pixelRatio,sizes.height * sizes.pixelRatio)), // 拥有尺寸,同时乘以像素比因为 要拥有相同比例
uShadowRepetitions:new THREE.Uniform(100),
uShadowColor:new THREE.Uniform(new THREE.Color(materialParameters.shadowColor)),
uLightRepetitions:new THREE.Uniform(130),
uLightColor:new THREE.Uniform(new THREE.Color(materialParameters.lightColor))
}
})
gui
.addColor(materialParameters, 'color')
.onChange(() =>
{
material.uniforms.uColor.value.set(materialParameters.color)
})
gui
.add(material.uniforms.uShadowRepetitions, 'value')
.min(1)
.max(300)
.step(1)
gui
.addColor(materialParameters, 'shadowColor')
.onChange(() =>
{
material.uniforms.uShadowColor.value.set(materialParameters.shadowColor)
})
gui
.add(material.uniforms.uLightRepetitions, 'value')
.min(1)
.max(300)
.step(1)
gui
.addColor(materialParameters, 'lightColor')
.onChange(() =>
{
material.uniforms.uLightColor.value.set(materialParameters.lightColor)
})
/**
* 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)
}
)
/**
* 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()
style.css
*
{
margin: 0;
padding: 0;
}
html,
body
{
overflow: hidden;
}
.webgl
{
position: fixed;
top: 0;
left: 0;
outline: none;
}
fragment.glsl
uniform vec3 uColor;
uniform vec2 uResolution;
uniform float uShadowRepetitions;
uniform vec3 uShadowColor;
uniform float uLightRepetitions;
uniform vec3 uLightColor;
varying vec3 vNormal;
varying vec3 vPosition;
#include ../includes/directionalLight.glsl
#include ../includes/ambientLight.glsl
vec3 halftone( // 创建半色调颜色函数
vec3 color, // 颜色
float repetitions, // 大小
vec3 direction, // 方向
float low, // 界限值
float high, // 界限值
vec3 pointColor, // 半色调颜色
vec3 normal // 法线
){
float insensity = dot(normal,direction); // 强度 这部分之前的代码做过,法线和光()的方向
insensity = smoothstep(low,high,insensity); // 平滑过渡
vec2 uv = gl_FragCoord.xy / uResolution.y ; // 完美的像素比(uResolution)
uv *= repetitions;
uv = mod(uv,1.0);
// 我们需要距离紫外线电池中心的距离。我们可以使用距离函数(distance)与vec2(0.5)相比
float point = distance(uv,vec2(0.5));
point = 1.0 - step(0.5 * insensity,point);
return mix(color,pointColor,point * 0.5); // 将颜色混合在一起,
}
/*
mod(x, y)返回x和y的模
利用取模函数mod()达到反复渐变效果
distance(p0,p1)计算向量p0,p1之间的距离
step(edge, x)如果x < edge,返回0.0,否则返回1.0
dot向量x,y之间的点积
平稳过渡
smoothstep(edge0, edge1, x)
如果x <= edge0,返回0.0 ;
如果x >= edge1 返回1.0;
如果edge0 < x < edge1,则执行0~1之间的平滑埃尔米特差值。
如果edge0 >= edge1,结果是未定义的
mix(x, y, a)返回线性混合的x和y,如:x*(1−a)+y*a
*/
void main()
{
vec3 viewDirection = normalize(vPosition - cameraPosition); // 光的反射 视野方向
vec3 normal = normalize(vNormal);
vec3 color = uColor;
// light
vec3 light = vec3(0.0);
light += ambientLight(
vec3(1.0),
1.0
);
light += directionalLight(
vec3(1.0,1.0,1.0),
1.0,
normal,
vec3(1.0,1.0,0.0),
viewDirection,
1.0
);
color *= light;
// Halftone 除以像素比 获得最终颜色
// float repetitions = 50.0;
// vec3 direction = vec3(0.0, -1.0 , 0.0); // 方向
// float low = -0.8; // 界限值 展示半色调
// float high = 1.5; // 界限值
// vec3 pointColor = vec3(1.0,0.0,0.0);
color = halftone(
color,
uShadowRepetitions,
vec3(0.0, -1.0 , 0.0),
-0.8,
1.5,
uShadowColor,
normal
);
color = halftone(
color,
uLightRepetitions,
vec3(1.0, 1.0 , 0.0),
0.5,
1.5,
uLightColor,
normal
);
// Final color
gl_FragColor = vec4(color, 1.0);
#include <tonemapping_fragment>
#include <colorspace_fragment>
}
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;
}
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);
vec3 lightReflection = reflect(- lightDirection, normal);
// Shading
float shading = dot(normal, lightDirection);
shading = max(0.0, shading);
// Specular
float specular = - dot(lightReflection, viewDirection);
specular = max(0.0, specular);
specular = pow(specular, specularPower);
return lightColor * lightIntensity * (shading + specular);
}
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);
vec3 lightDirection = normalize(lightDelta);
vec3 lightReflection = reflect(- lightDirection, normal);
// Shading
float shading = dot(normal, lightDirection);
shading = max(0.0, shading);
// 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);
}
二、效果
半色调颜色-阴影 着色器
总结
半色调颜色 着色器的应用!
原文地址:https://blog.csdn.net/weixin_41722662/article/details/144749817
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!