视觉基础篇14 # 如何使用片元着色器进行几何造型?
Posted 凯小默
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了视觉基础篇14 # 如何使用片元着色器进行几何造型?相关的知识,希望对你有一定的参考价值。
说明
【跟月影学可视化】学习笔记。
如何用片元着色器控制局部颜色?
把图片绘制为纯黑色:
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
gl_FragColor = vec4(0, 0, 0, 1);
`;
根据纹理坐标值来绘制,让某个图案的颜色,从左到右由黑向白过渡
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
gl_FragColor.rgb = vec3(vUv.x);
gl_FragColor.a = 1.0;
`;
使用乘法创造一个 10*10 的方格,让每个格子左上角是绿色,右下角是红色,中间是过渡色。
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
vec2 st = vUv * 10.0;
gl_FragColor.rgb = vec3(fract(st), 0.0);
gl_FragColor.a = 1.0;
`;
通过 idx = floor(st)
获取网格的索引,判断网格索引除以 2 的余数(奇偶性),根据它来决定是否翻转网格内的 x、y 坐标。
<!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>如何用片元着色器控制局部颜色?</title>
<style>
canvas
border: 1px dashed salmon;
</style>
</head>
<body>
<canvas width="512" height="512"></canvas>
<script src="./common/lib/gl-renderer.js"></script>
<script>
const vertex = `
attribute vec2 a_vertexPosition;
attribute vec2 uv;
varying vec2 vUv;
void main()
gl_PointSize = 1.0;
vUv = uv;
gl_Position = vec4(a_vertexPosition, 1, 1);
`;
// // 把图片绘制为纯黑色
// const fragment = `
// #ifdef GL_ES
// precision highp float;
// #endif
// varying vec2 vUv;
// void main()
// gl_FragColor = vec4(0, 0, 0, 1);
//
// `;
// // 根据纹理坐标值来绘制,让某个图案的颜色,从左到右由黑向白过渡
// const fragment = `
// #ifdef GL_ES
// precision highp float;
// #endif
// varying vec2 vUv;
// void main()
// gl_FragColor.rgb = vec3(vUv.x);
// gl_FragColor.a = 1.0;
//
// `;
// // 使用乘法创造一个 10*10 的方格,让每个格子左上角是绿色,右下角是红色,中间是过渡色。
// const fragment = `
// #ifdef GL_ES
// precision highp float;
// #endif
// varying vec2 vUv;
// void main()
// vec2 st = vUv * 10.0;
// gl_FragColor.rgb = vec3(fract(st), 0.0);
// gl_FragColor.a = 1.0;
//
// `;
// 通过 idx = floor(st) 获取网格的索引,判断网格索引除以 2 的余数(奇偶性),根据它来决定是否翻转网格内的 x、y 坐标。
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
vec2 st = vUv * 10.0;
vec2 idx = floor(st);
vec2 grid = fract(st);
vec2 t = mod(idx, 2.0);
if(t.x == 1.0)
grid.x = 1.0 - grid.x;
if(t.y == 1.0)
grid.y = 1.0 - grid.y;
gl_FragColor.rgb = vec3(grid, 0.0);
gl_FragColor.a = 1.0;
`;
const canvas = document.querySelector("canvas");
const renderer = new GlRenderer(canvas);
// 加载片元着色器并创建程序
const program = renderer.compileSync(fragment, vertex);
renderer.useProgram(program);
// 将顶点数据送入缓冲区
renderer.setMeshData([
positions: [
[-1, -1],
[-1, 1],
[1, 1],
[1, -1],
],
attributes:
uv: [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
],
,
cells: [
[0, 1, 2],
[2, 0, 3],
],
,
]);
// 渲染
renderer.render();
</script>
</body>
</html>
如何用片元着色器绘制圆、线段和几何图形
绘制圆
绘制一个模糊的圆
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
floatd = distance(vUv, vec2(0.5));
gl_FragColor.rgb = d * vec3(1.0);
gl_FragColor.a = 1.0;
`;
绘制一个清晰的圆
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
float d = distance(vUv, vec2(0.5));
gl_FragColor.rgb = step(d, 0.2) * vec3(1.0);
gl_FragColor.a = 1.0;
`;
因为浮点数计算的精度导致的锯齿现象。用 smoothstep 代替 step 即可解决这种问题。smoothstep 在 step-start 和 step-end
之间有一个平滑过渡的区间。
<!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>片元着色器绘制圆</title>
<style>
canvas
border: 1px dashed salmon;
</style>
</head>
<body>
<canvas width="512" height="512"></canvas>
<script src="./common/lib/gl-renderer.js"></script>
<script>
const vertex = `
attribute vec2 a_vertexPosition;
attribute vec2 uv;
varying vec2 vUv;
void main()
gl_PointSize = 1.0;
vUv = uv;
gl_Position = vec4(a_vertexPosition, 1, 1);
`;
// // 模糊的圆
// const fragment = `
// #ifdef GL_ES
// precision highp float;
// #endif
// varying vec2 vUv;
// void main()
// float d = distance(vUv, vec2(0.5));
// gl_FragColor.rgb = d * vec3(1.0);
// gl_FragColor.a = 1.0;
//
// `;
// // 清晰的圆
// const fragment = `
// #ifdef GL_ES
// precision highp float;
// #endif
// varying vec2 vUv;
// void main()
// float d = distance(vUv, vec2(0.5));
// gl_FragColor.rgb = step(d, 0.2) * vec3(1.0);
// gl_FragColor.a = 1.0;
//
// `;
// 清晰的圆无锯齿
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
float d = distance(vUv, vec2(0.5));
gl_FragColor.rgb = smoothstep(d, d + 0.01, 0.2) * vec3(1.0);
gl_FragColor.a = 1.0;
`;
const canvas = document.querySelector("canvas");
const renderer = new GlRenderer(canvas);
// 加载片元着色器并创建程序
const program = renderer.compileSync(fragment, vertex);
renderer.useProgram(program);
// 将顶点数据送入缓冲区
renderer.setMeshData([
positions: [
[-1, -1],
[-1, 1],
[1, 1],
[1, -1],
],
attributes:
uv: [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
],
,
cells: [
[0, 1, 2],
[2, 0, 3],
],
,
]);
// 渲染
renderer.render();
</script>
</body>
</html>
实现图片的渐显渐隐效果
上一节我们实现了图片粒子化,下面利用绘制圆实现图片的渐显渐隐效果
<!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>片元着色器绘制圆实现图片的渐显渐隐效果</title>
<style>
canvas
border: 1px dashed salmon;
</style>
</head>
<body>
<canvas width="1920" height="1080"></canvas>
<script src="./common/lib/gl-renderer.js"></script>
<script>
const vertex = `
attribute vec2 a_vertexPosition;
attribute vec2 uv;
varying vec2 vUv;
void main()
gl_PointSize = 1.0;
vUv = uv;
gl_Position = vec4(a_vertexPosition, 1, 1);
`;
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D tMap;
uniform vec2 uResolution;
uniform float uTime;
varying vec2 vUv;
float random (vec2 st)
return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
void main()
vec2 uv = vUv;
uv.y *= uResolution.y / uResolution.x;
vec2 st = uv * 100.0;
float d = distance(fract(st), vec2(0.5));
float p = uTime + random(floor(st));
float shading = 0.5 + 0.5 * sin(p);
d = smoothstep(d, d + 0.01, 1.0 * shading);
vec4 color = texture2D(tMap, vUv);
gl_FragColor.rgb = color.rgb * clamp(0.5, 1.3, d + 1.0 * shading);
gl_FragColor.a = color.a;
`;
const canvas = document.querySelector("canvas");
const renderer = new GlRenderer(canvas);
// 加载片元着色器并创建程序
const program = renderer.compileSync(fragment, vertex);
renderer.useProgram(program);
(async function ()
const texture = await renderer.loadTexture('./assets/img/flower.jpg');
renderer.uniforms.tMap = texture;
renderer.uniforms.uResolution = [canvas.width, canvas.height];
renderer.uniforms.uTime = 0;
// 将顶点数据送入缓冲区
renderer.setMeshData([
positions: [
[-1, -1],
[-1, 1],
[1, 1],
[1, -1],
],
attributes:
uv: [
[0, 0],
[0, 1],
[1, 1],
[1, 0],
],
,
cells: [[0, 1, 2], [2, 0, 3]],
]);
renderer.render();
function update(t)
renderer.uniforms.uTime = t / 500;
requestAnimationFrame(update);
update(0);
());
</script>
</body>
</html>
绘制线
计算点到直线(向量)的距离即可。
<!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>片元着色器绘制圆</title>
<style>
canvas
border: 1px dashed salmon;
</style>
</head>
<body>
<canvas width="512" height="512"></canvas>
<script src="./common/lib/gl-renderer.js"></script>
<script>
const vertex = `
attribute vec2 a_vertexPosition;
attribute vec2 uv;
varying vec2 vUv;
void main()
gl_PointSize = 1.0;
vUv = uv;
gl_Position = vec4(a_vertexPosition, 1, 1);
`;
// 画出一条斜线
const fragment = `
#ifdef GL_ES
precision highp float;
#endif
varying vec2 vUv;
void main()
vec3 line = vec3(1, 1, 0);
float d = abs(cross(vec3(vUv,0), normalize(line)).z);
gl_FragColor.rgb = (1.0 - smoothstep(0.0, 0.01, d)) * vec3(1.0);
gl_FragColor.a = 1.0;
`;
const canvas = document.querySelector("canvas");
const renderer = new GlRenderer(canvas);
// 加载片元着色器并创建程序
const program = renderer.compileSync(fragment, vertex);
renderer.useProgram(program);
// 将顶点数据送入缓冲区
renderer.setMeshData([
positions: [
[-1, -1],
[-1, 1],
[1, 1],
[1, -1],
],
attributes:
uv: [
[0, 0],
[0, 1],
[1, 1以上是关于视觉基础篇14 # 如何使用片元着色器进行几何造型?的主要内容,如果未能解决你的问题,请参考以下文章
OpenGL渲染管线(rendering pipeline)