问题背景
需要用webgl 优化 kriging.grid 物理计算量大的问题
解决思路
借助 GPU 的性能,采用 webgl 替代克里金的计算量大的方法
demo案例效果
矩阵转换–GLSL着色程序
注意glsl语句
void main() {
// 将位置和矩阵相乘
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
// 从裁减空间转换到颜色空间
// 裁减空间范围 -1.0 到 +1.0
// 颜色空间范围 0.0 到 1.0 RGBA
v_color = gl_Position * 0.5 + 0.5;// 实现 b 固定为0.5 ,a 固定为 1
// v_color = vec4(gl_Position.x * 0.5 + 0.5,gl_Position.y * 0.5 + 0.5,0,1); // 改成b固定为0,a固定为 1 实现 越往上 g 越大,越往右 r 越大
}
让我们从上例的三个顶点开始分析
我们的给顶点着色器施加了一个包含平移,旋转和缩放的的矩阵,并将结果转换到裁剪空间。 默认平移,旋转和缩放值为:平移 = 200, 150
,旋转 = 0
,缩放 = 1
,所以这里只进行了平移。 画布大小(背景缓冲)为 400×300
,所以三个顶点在裁剪空间中为以下坐标值。
写入 gl_Position
的值
同时将这些值转换到颜色空间中赋给我们定义的可变量v_color。
写入 v_color 的值
注意
:这个怎么来的呢
利用这三个值进行插值后传进每个像素运行的片段着色器中。
index.vue
<!--/**
* @author: liuk
* @date: 2024-09-04
* @describe:webgl 工作原理
*/-->
<template>
<div class="workingPrinciple1-box">
<div class="block">
<el-form-item label="x:" label-width="50">
<el-slider v-model="translationx" :max="gl.canvas.width" @change="drawScene"/>
</el-form-item>
<el-form-item label="y:" label-width="50">
<el-slider v-model="translationy" :max="gl.canvas.height" @change="drawScene"/>
</el-form-item>
<el-form-item label="angle:" label-width="50">
<el-slider v-model="angleInRadians" :max="360" @change="drawScene"/>
</el-form-item>
<el-form-item label="scalex:" label-width="50">
<el-slider v-model="scalex" :min="-5" :max="5" :step="0.01" @change="drawScene"/>
</el-form-item>
<el-form-item label="scaley:" label-width="50">
<el-slider v-model="scaley" :min="-5" :max="5" :step="0.01" @change="drawScene"/>
</el-form-item>
</div>
</div>
</template>
<script lang="ts" setup>
import {useWebglDemoStore} from "@/store/modules/webglDemo";
import {onBeforeUnmount, onMounted, reactive, toRefs} from "vue";
import m3 from "@/utils/m3.js"
const model = reactive({
translationx: 200,
translationy:150,
angleInRadians: 0,
scalex: 1,
scaley: 1,
})
const {translationx, translationy, angleInRadians, scalex, scaley} = toRefs(model)
const webglDemoStore = useWebglDemoStore()
const gl = webglDemoStore.gl
/*
* 概念:
* WebGL在GPU上的工作基本上分为两部分
* 1.将顶点(或数据流)转换到裁剪空间坐标
* 2.基于第一部分的结果绘制像素点 - ”光栅化“
* */
onMounted(() => {
init()
})
onBeforeUnmount(() => {
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
})
// 初始化
let program, positionBuffer, positionAttributeLocation, matrixLocation
const init = () => {
// 创建两个着色器
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
// 创建着色程序
program = createProgram(gl, vertexShader, fragmentShader)
// 从刚才创建的GLSL着色程序中找到 a_position 属性值所在的位置
positionAttributeLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(positionAttributeLocation);
// 全局变量所在的位置
matrixLocation = gl.getUniformLocation(program, "u_matrix");
// 创建缓冲
positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 三个二维点坐标,把 gl.bufferData 复制这些数据到GPU的 positionBuffer 对象上
const positions = [0, -100, 150, 125, -175, 100];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
drawScene()
}
// 创建GLSL字符串 - 多行模板文字方式
const vertexShaderSource = `
attribute vec2 a_position;
uniform mat3 u_matrix;
varying vec4 v_color;
void main() {
// 将位置和矩阵相乘
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
// 从裁减空间转换到颜色空间
// 裁减空间范围 -1.0 到 +1.0
// 颜色空间范围 0.0 到 1.0 RGBA
// v_color = gl_Position * 0.5 + 0.5;// 实现 b 固定为0.5 ,a 固定为 1
v_color = vec4(gl_Position.x * 0.5 + 0.5,gl_Position.y * 0.5 + 0.5,0,1); // 改成b固定为0,a固定为 1 实现 越往上 g 越大,越往右 r 越大
}
`
const fragmentShaderSource = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`
// 创建着色器方法,输入参数:渲染上下文,着色器类型,数据源
const createShader = (gl, type, source) => {
const shader = gl.createShader(type); // 创建着色器对象
gl.shaderSource(shader, source); // 提供数据源
gl.compileShader(shader); // 编译 -> 生成着色器
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.warn(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
// 创建着色程序,将这两个着色器 link(链接)到一个 program(着色程序)
const createProgram = (gl, vertexShader, fragmentShader) => {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.warn(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
const drawScene = () => { // 渲染
// 调整 canvas 尺寸
gl.canvas.width = gl.canvas.clientWidth;
gl.canvas.height = gl.canvas.clientHeight;
// 渲染
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// 清空画布
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 告诉它用我们之前写好的着色程序(一个着色器对)
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
const size = 2; // 每次迭代运行提取两个单位数据
const type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
const normalize = false; // 不需要归一化数据
const stride = 0; // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
// 每次迭代运行运动多少内存到下一个数据开始点
const offset = 0; // 从缓冲起始位置开始读取
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset)
// Compute the matrix
console.log(model.translationx, model.translationy)
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, model.translationx, model.translationy);
matrix = m3.rotate(matrix, model.angleInRadians);
matrix = m3.scale(matrix, model.scalex, model.scaley);
// Set the matrix.
gl.uniformMatrix3fv(matrixLocation, false, matrix);
// WebGL运行我们的GLSL着色程序
const primitiveType = gl.TRIANGLES; //图元类型
const offset1 = 0;
const count = 3; // 顶点着色器将运行三次
gl.drawArrays(primitiveType, offset1, count);
}
</script>
<style lang="scss" scoped>
.workingPrinciple1-box {
position: absolute;
right: 20px;
top: 80px;
width: 250px;
padding: 10px;
box-sizing: border-box;
background: #ccc;
}
:deep(.el-form-item) {
margin-bottom: 0;
.el-form-item__label {
color: #000 !important;
}
}
</style>