предисловие
это обучениеthree.js系列
Четвертая, первые три:
Напишите анимацию дождя с помощью three.js
Напишите небольшую сцену с помощью three.js
Напишите отражающий мяч с помощью three.js
оthree.js
Базовые знания размещены в первой статье:Напишите анимацию дождя с помощью three.js, можете идти проверять, следующие случаи не повторятся.
从今天开始学习着色器,下面是一个简单使用着色器动画完成的3D地球。 Давайте начнем.
Начало работы с шейдерами
Графика рисования Webgl — это механизм рисования на основе шейдеров. Шейдеры предоставляют гибкий и мощный метод рисования двухмерной или трехмерной графики. Все программы Webgl должны использовать его.
Язык шейдеров похож на язык C, когда мы пишем программы webgl, язык шейдеров встраивается в язык javascript в виде строк.
Например, чтобы нарисовать точку на экране, код выглядит следующим образом:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
margin: 0
}
</style>
</head>
<body>
<canvas id="webgl"></canvas>
</body>
<script>
//将canvas的大小设置为屏幕大小
var canvas = document.getElementById('webgl')
canvas.height = window.innerHeight
canvas.width = window.innerWidth
//获取webgl绘图上下文
var gl = canvas.getContext('webgl')
//将背景色设置为黑色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
//顶点着色器代码(字符串形式)
var VSHADER_SOURCE =
`void main () {
gl_Position = vec4(0.5, 0.5, 0.0, 1.0); //点的位置:x: 0.5, y: 0.5, z: 0。齐次坐标
gl_PointSize = 10.0; //点的尺寸,非必须,默认是0
}`
//片元着色器代码(字符串形式)
var FSHADER_SOURCE =
`void main () {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); //点的颜色:四个量分别代表 rgba
}`
//初始化着色器
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
//绘制一个点,第一个参数为gl.POINTS
gl.drawArrays(gl.POINTS, 0, 1)
function initShaders(gl, vshader, fshader) {
var program = createProgram(gl, vshader, fshader);
if (!program) {
console.log('Failed to create program');
return false;
}
gl.useProgram(program);
gl.program = program;
return true;
}
function createProgram(gl, vshader, fshader) {
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
if (!vertexShader || !fragmentShader) {
return null;
}
var program = gl.createProgram();
if (!program) {
return null;
}
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
var error = gl.getProgramInfoLog(program);
console.log('Failed to link program: ' + error);
gl.deleteProgram(program);
gl.deleteShader(fragmentShader);
gl.deleteShader(vertexShader);
return null;
}
return program;
}
function loadShader(gl, type, source) {
// 创建着色器对象
var shader = gl.createShader(type);
if (shader == null) {
console.log('unable to create shader');
return null;
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var error = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
return null;
}
return shader;
}
</script>
</html>
Приведенный выше код рисует точку в правой верхней части экрана.
Для рисования этой точки требуется три необходимых элемента информации: положение, размер и цвет.
- Вершинный шейдер определяет положение и размер точки. (В приведенном ниже коде
gl_Position
,gl_PointSize
а такжеgl_FragColor
Обе являются встроенными глобальными переменными шейдера. )
var VSHADER_SOURCE =
`void main () {
gl_Position = vec4(0.5, 0.5, 0.0, 1.0); //指定点的位置
gl_PointSize = 10.0; //指定点的尺寸
}`
- Фрагментный шейдер определяет цвет точки.
var FSHADER_SOURCE =
`void main () {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); //指定点的颜色
}`
атрибутивные переменные и юниформ-переменные
В примере выше мы указали положение, размер и цвет точки непосредственно в шейдере. В реальной работе эта информация в основном передается шейдеру с помощью js.
Переменные, используемые для связи между кодом js и кодом шейдера,attribute变量
а такжеuniform变量
.
Какую переменную использовать, зависит от самих данных, которые необходимо передать,attribute变量
используется для передачи данных, связанных с вершиной,uniform变量
Используется для передачи независимых от вершин данных.
В следующем примере координаты точки, которую нужно нарисовать, будут переданы через js.
//顶点着色器
var VSHADER_SOURCE =
`attribute vec4 a_Position; //声明一个attribute变量a_Position,用于接受js传递的顶点位置
void main () {
gl_Position = a_Position; //将a_Position赋值给gl_Position
gl_PointSize = 10.0;
}`
//片元着色器
var FSHADER_SOURCE =
`void main () {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}`
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)
//js代码中,获取a_Position的存储位置,并向其传递数据
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttrib3f(a_Position, 0.5, 0.5, 0.0)
gl.drawArrays(gl.POINTS, 0, 1)
переменная переменная
Мы проходим от JS Shader, как правило, связанные данные вершины вершины, например, мы хотим нарисовать треугольник, а положение вершины и цвет вершины треугольника передаются через js. Положение трех вершин может определить положение треугольника, так что же определяет цвет всего треугольника?
Это требует появления различных переменных.
Расчет цвета в webgl:
существует顶点着色器
, получить данные о положении и цвете каждой вершины, переданной js. Система webgl будет, основываясь на данных вершин,插值计算
Снаружи, в области между вершинами, значение цвета каждого фрагмента (под которым можно понимать наименьшую точку рендеринга, из которой состоит изображение). Расчет интерполяции выполняется автоматически системой webgl.
Вычислите значение цвета каждого фрагмента, а затем передайте его в片元着色器
.片元着色器
Визуализирует изображение на основе значения цвета каждого фрагмента.
от顶点着色器
прибыть片元着色器
, пропуская работу мимоvarying变量
Заканчивать.
код показывает, как показано ниже.
- Код вершины шейдера
var VSHADER_SOURCE =
`attribute vec4 a_Position; //顶点位置
attribute vec4 a_Color; //顶点颜色
varying vec4 v_Color; //根据顶点颜色,计算出三角形中每个片元的颜色值,然后将每个片元的颜色值传递给片元着色器。
void main () {
gl_Position = a_Position;
v_Color = a_Color; // a_Color 赋值给 v_Color
}`
- Код фрагментного шейдера
var FSHADER_SOURCE =
`precision mediump float;
varying vec4 v_Color; //每个片元的颜色值
void main () {
gl_FragColor = v_Color;
}`
- js-код
var verticesColors = new Float32Array([ //顶点位置和颜色
0.0, 0.5, 1.0, 0.0, 0.0, // 第一个点,前两个是坐标(x,y; z默认是0),后三个是颜色
-0.5, -0.5, 0.0, 1.0, 0.0, // 第二个点
0.5, -0.5, 0.0, 0.0, 1.0 // 第三个点
])
//以下是通过缓冲区向顶点着色器传递顶点位置和颜色
var vertexColorBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW)
var FSIZE = verticesColors.BYTES_PER_ELEMENT
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
gl.enableVertexAttribArray(a_Position)
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2)
gl.enableVertexAttribArray(a_Color)
//绘制一个三角形,第一个参数为gl.TRIANGLES
gl.drawArrays(gl.TRIANGLES, 0, 3)
Окончательный эффект отрисовки выглядит следующим образом:
Простое понимание наложения текстур
В приведенном выше примере мы присваиваем значения цвета каждой вершине.
Чтобы расширить, отображение текстуры заключается в указании координат текстуры для каждой вершины, а затем система webgl будет интерполировать и вычислять координаты текстуры каждого фрагмента на основе координат текстуры вершины.
Затем в фрагментном шейдере, согласно изображению входящей текстуры и координаты текстуры каждого фрагмента, значение цвета (Texel) на соответствующей координате текстуры в текстуру изображения вынимается как цветное значение фрагмента, а также для рендера.
Особенности текстурных координат:
- Нижний левый угол изображения текстуры — это начало координат (0, 0).
- Вправо — это положительное направление горизонтальной оси, а максимальное значение горизонтальной оси равно 1 (правый край изображения).
- Вверх — это положительное направление вертикальной оси, а максимальное значение вертикальной оси равно 1 (верхний край изображения).
Независимо от размера изображения текстуры диапазон координат текстуры: ось x: 0-1, ось y: 0-1
Нарисуйте трехмерную Землю
Используя webgl для рисования, шаги и API относительно громоздки, но, к счастью, мы можем использоватьthree.js
.
three.js
серединаShaderMaterial
Мы можем создавать свои собственные шейдеры и напрямую манипулировать пикселями. Нам просто нужно понять основы шейдеров.
Начните рисовать землю.
базовая сфера
Нарисовать базовую сферу относительно просто, используяthree.js
Материал, предусмотренный на линии. На основе материала, вНапишите отражающий мяч с помощью three.jsЕсть более подробное введение.
var loader = new THREE.TextureLoader()
var group = new THREE.Group()
//创建本体
var geometry = new THREE.SphereGeometry(20,30,30) //创建球形几何体
var earthMaterial = new THREE.MeshPhongMaterial({ //创建材质
map: loader.load( './images/earth.png' ), //基础纹理
specularMap: loader.load('./images/specular.png'), //高光纹理,指定物体表面中哪部分比较闪亮,哪部分相对暗淡
normalMap: loader.load('./images/normal.png'), //法向纹理,创建更加细致的凹凸和褶皱
normalScale: new THREE.Vector2(3, 3)
})
var sphere = new THREE.Mesh(geometry, earthMaterial) //创建基础球体
group.add(sphere)
мобильная атмосфера
использоватьShaderMaterial
Пользовательские шейдеры. Поток атмосферы через каждыйrequestAnimationFrame
Измените реализацию координат текстуры в цикле рендеринга. Чтобы сделать поток более естественным, добавьте шум.
//顶点着色器
var VSHADER_SOURCE = `
varying vec2 v_Uv;
void main () {
v_Uv = uv; //顶点纹理坐标
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 );
}
`
//片元着色器
var FSHADER_SOURCE = `
uniform float time; //时间变量
uniform sampler2D fTexture; //大气纹理图像
uniform sampler2D nTexture; //噪声纹理图像
varying vec2 v_Uv; //片元纹理坐标
void main () {
vec2 new_Uv= v_Uv + vec2( 0, 0.02 ) * time; //向量加法,根据时间变量计算新的纹理坐标
//利用噪声随机使纹理坐标随机化
vec4 noise_Color = texture2D( nTexture, new_Uv );
new_Uv.x += noise_Color.r * 0.2;
new_Uv.y += noise_Color.g * 0.2;
gl_FragColor = texture2D( fTexture, new_Uv ); //提取大气纹理图像的颜色值(纹素)
}
`
var flowTexture = loader.load('./images/flow.png')
flowTexture.wrapS = THREE.RepeatWrapping
flowTexture.wrapT = THREE.RepeatWrapping
var noiseTexture = loader.load('./images/noise.png')
noiseTexture.wrapS = THREE.RepeatWrapping
noiseTexture.wrapT = THREE.RepeatWrapping
//着色器材质
var flowMaterial = new THREE.ShaderMaterial({
uniforms: {
fTexture: {
value: flowTexture,
},
nTexture: {
value: noiseTexture,
},
time: {
value: 0.0
},
},
// 顶点着色器
vertexShader: VSHADER_SOURCE,
// 片元着色器
fragmentShader: FSHADER_SOURCE,
transparent: true
})
var fgeometry = new THREE.SphereGeometry(20.001,30,30) //创建比基础球体略大的球状几何体
var fsphere = new THREE.Mesh(fgeometry, flowMaterial) //创建大气球体
group.add(fsphere)
scene.add( group )
созданныйgroup
, базовая сфера и большой воздушный шар добавляются кgroup
, в целом задайте вращение и положение, оба напрямую изменяютgroup
характеристики.
var clock = new THREE.Clock()
//渲染循环
var animate = function () {
requestAnimationFrame(animate)
var delta = clock.getDelta()
group.rotation.y -= 0.002 //整体转动
flowMaterial.uniforms.time.value += delta //改变uniforms.time的值,用于片元着色器中的纹理坐标计算
renderer.render(scene, camera)
}
animate()
гало
Спрайт используется для создания ореола.Спрайт представляет собой плоскость, которая всегда обращена к камере.Здесь он используется для имитации ореола.Независимо от того, как вращается сфера, она всегда оказывается в ореоле.
var ringMaterial = new THREE.SpriteMaterial( { //创建点精灵材质
map: loader.load('./images/ring.png')
} )
var sprite = new THREE.Sprite( ringMaterial ) //创建精灵,和普通物体的创建不一样
sprite.scale.set(53,53, 1) //设置精灵的尺寸
scene.add( sprite )
Финальные рендеры: