/// <reference path="../../../../scripts/graphics/webgl/regl/2.1.0/regl.d.ts" />
/// <reference path="../../../../scripts/mathematicsAndPhysics/linearAlgebra/matrix/gl-matrix/3.4.3/dist/index.d.ts" />
/// <reference path="../../../../scripts/graphics/3DGraphics/three.js/r148/types/index.d.ts" />
// vbo fbo ebo vao
const glsl = {
//
highp: 'highp',// // Math.pow(2,16)
mediump: 'mediump',// Math.pow(2,10)
lowp: 'lowp', // Math.pow(2,8)
void: 'void',
int: 'int',
float: 'float', // array float f_list[3]
bool: 'bool',
vec2: 'vec2',
vec3: 'vec2',
vec4: 'vec4',
bvec2: 'bvec2',
bvec3: 'bvec2',
bvec4: 'bvec4',
ivec2: 'ivec2',
ivec3: 'ivec2',
ivec4: 'ivec4',
mat2: 'mat2',
mat3: 'mat3',
mat4: 'mat4',
sampler2D: 'sampler2D',
samplerCube: 'samplerCube',
/**s
* struct type-name {
members
}
struct-name[]
*/
struct: 'struct',
const: 'const',
attribute: 'attribute',
uniform: 'uniform',
varying: 'varying',
vert: {
gl_Position: 'gl_Position', // highp vec4
gl_PointSize: 'gl_PointSize', // mediump float
},
frag: {
// input
gl_FragCoord: 'gl_FragCoord',// mediump vec4
gl_FrontFacing: 'gl_FrontFacing', // bool 碎片属于前向原语
gl_PointCoord: 'gl_PointCoord', // mediump vec2
// output
gl_FragColor: 'gl_FragColor', //mediump vec4
gl_FragData: 'gl_FragData[n]',// mediump vec4
},
builtFunc: {
// Texture Lookup Functions [8.7]Available only in vertex shaders.
texture2DLod: (sampler, coord, lod) => { return `texture2DLod(${sampler},${coord},${lod})` },// vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod)
texture2DProjLod: (sampler, coord, lod) => { return `texture2DProjLod(${sampler},${coord},${lod})` },// vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod)
// texture2DProjLod:(sampler,coord,lod)=>{ return `texture2DProjLod(${sampler},${coord},${lod})`},// vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod)
textureCubeLod: (sampler, coord, lod) => { return `textureCubeLod(${sampler},${coord},${lod})` },// vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod)
// Available only in fragment shaders.
texture2D: (sampler, coord, bias) => { return `texture2D(${sampler},${coord}${bias !== undefined ? ',' + bias : ''})` }, // vec4 texture2D(sampler2D sampler, vec2 coord, float bias)
texture2DProj: (sampler, coord, bias) => { return `texture2D(${sampler},${coord}${bias !== undefined ? ',' + bias : ''})` },// vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias)
// texture2D:(sampler,coord,bias)=>{ return `texture2D(${sampler},${coord},${bias})`},// vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias)
textureCube: (samplerCube, coord, bias) => { return `textureCube(${samplerCube},${coord}${bias !== undefined ? ',' + bias : ''})` }, // vec4 textureCube(samplerCube sampler, vec3 coord, float bias)
// Available in vertex and fragment shaders.
// vec4 texture2D(sampler2D sampler, vec2 coord)
// vec4 texture2DProj(sampler2D sampler, vec3 coord)
// vec4 texture2DProj(sampler2D sampler, vec4 coord)
// vec4 textureCube(samplerCube sampler, vec3 coord)
},
// * /** gl.POINTS */
// "points" |
// /** gl.LINES */
// "lines" |
// /** gl.LINE_STRIP */
// "line strip" |
// /** gl.LINE_LOOP */
// "line loop" |
// /** gl.TRIANGLES */
// "triangles" |
// /** gl.TRIANGLE_STRIP */
// "triangle strip" |
// /** gl.TRIANGLE_FAN */
// "triangle fan";
primitive: {
points: 'points',
lines: 'lines',
lineStrip: 'line strip',
lineLoop: 'line loop',
triangles: 'triangles',
triangleStrip: 'triangle strip',
triangleFan: 'triangle fan'
}
}
let { vec2, vec3, vec4, mat2, mat2d, mat3, mat4, quat, quat2 } = glMatrix
class Object3D {
type = "Object3D"
constructor() {
this.position = vec3.fromValues(0, 0, 0)
this.scale = vec3.fromValues(1, 1, 1)
this.rotation = {
_x: 0,
_y: 0,
_z: 0
} //欧拉角
this.quaternion = quat.fromValues(0, 0, 0, 1)
this.matrixWorld = mat4.create() // 对象的世界矩阵 应用在相机上,就是相机矩阵
this.matrix = mat4.create() // 局部矩阵
let updateQuaternion = () => {
quat.fromEuler(this.quaternion, this.rotation.x, this.rotation.y, this.rotation.z)
}
Object.defineProperties(this.rotation, {
x: {
get() {
return this._x
},
set(v) {
this._x = v
updateQuaternion()
}
},
y: {
get() {
return this._y
},
set(v) {
this._y = v
updateQuaternion()
}
},
z: {
get() {
return this._z
},
set(v) {
this._z = v
updateQuaternion()
}
}
})
}
updateMatrix() {
// mat4.translate(this.matrix,this.matrix,this.position)
// mat4.rotateX(this.matrix,this.matrix,this.rotation[0])
// mat4.rotateY(this.matrix,this.matrix,this.rotation[1])
// mat4.rotateZ(this.matrix,this.matrix,this.rotation[2])
// mat4.scale(this.matrix,this.matrix,this.scale)
// 右乘 translate*rotation*scale
// 左乘 scale*rotation*translate
mat4.fromRotationTranslationScale(this.matrix, this.quaternion, this.position, this.scale)
}
updateMatrixWorld() {
this.updateMatrix()
mat4.identity(this.matrixWorld)
mat4.multiply(this.matrixWorld, this.matrixWorld, this.matrix)
}
setPoisition(x, y, z) {
vec3.set(this.position, x, y, z)
}
setScale(x, y, z) {
vec3.set(this.scale, x, y, z)
}
setQuaternion(x, y, z, w = 1) {
quat.set(this.quaternion, x, y, z, w)
}
rotateX(radian) {
quat.rotateX(this.quaternion, this.quaternion, radian)
}
rotateY(radian) {
quat.rotateY(this.quaternion, this.quaternion, radian)
}
rotateZ(radian) {
quat.rotateZ(this.quaternion, this.quaternion, radian)
}
getWorldQuaternion(target) {
this.updateMatrixWorld()
let result = target || quat.create()
mat4.getRotation(result, this.matrixWorld)
return result
}
getWorldDirection2(result) {
let quaternion = this.getWorldQuaternion()
result = result || vec3.fromValues(0, 0, 1)
vec3.transformQuat(result, result, quaternion)
return result
}
getWorldDirection(result) {
this.updateMatrixWorld()
let e = this.matrixWorld
result = result || vec3.fromValues(0, 0, 1)
let direction = vec3.fromValues(e[8], e[9], e[10])
vec3.normalize(result, direction)
return result
}
lookAt = (function () {
let cameraMatrix = mat4.create()
let viewMatrix = mat4.create()
let position = vec3.create()
/**
* @this {Object3D}
*/
return function (target) {
this.updateMatrixWorld()
if (this.type == 'Camera') {
// 获取相机世界位置
mat4.getTranslation(position, this.matrixWorld)
// 用look at计算相机矩阵。
mat4.lookAt(cameraMatrix, position, target, this.up)
// 由相机矩阵得出视图矩阵
// 视图矩阵是将所有物体以相反于相机的方向运动, 尽管相机还是在原点但是相对关系是期望的。我们可以使用 inverse 方法计算逆矩阵( 完全对立的转换矩阵)
mat4.invert(viewMatrix, cameraMatrix)// threejs的lookat(反转了)与gl-matrix的不一样.
// 更新视图矩阵到四元数上
mat4.getRotation(this.quaternion, viewMatrix)
// 更新
this.updateMatrixWorld()
} else {
// 获取相机世界位置
mat4.getTranslation(position, this.matrixWorld)
// 用look at计算相机矩阵。
mat4.lookAt(cameraMatrix, target, position, this.up)
// 由相机矩阵得出视图矩阵
mat4.invert(viewMatrix, cameraMatrix)
// 更新视图矩阵到四元数上
mat4.getRotation(this.quaternion, viewMatrix)
// 更新
this.updateMatrixWorld()
}
}
}())
}
class Camera extends Object3D {
type = "Camera"
constructor(fov = 50, aspect = 1, near = 0.001, far = 1000) {
super()
this.fov = fov
this.aspect = aspect
this.near = near
this.far = far
this.up = vec3.fromValues(0, 1, 0)
// 从viewMatrix 转换到quaternion再到matrix z轴会变为正
this.matrixWorldInvert = mat4.create() // 视图矩阵 观察空间矩阵
this.projectionMatrix = mat4.create() // 投影矩阵
this.projectionMatrixInverse = mat4.create()
this.updateProjectionMatrix()
}
getWorldDirection2(result) {
let quaternion = this.getWorldQuaternion()
result = result || vec3.fromValues(0, 0, -1)
vec3.transformQuat(result, result, quaternion)
return result
}
getWorldDirection(result) {
this.updateMatrixWorld()
let e = this.matrixWorld
result = result || vec3.fromValues(0, 0, 0)
let direction = vec3.fromValues(-e[8], -e[9], -e[10])
vec3.normalize(result, direction)
return result
}
updateMatrixWorld() {
super.updateMatrixWorld()
mat4.invert(this.matrixWorldInvert, this.matrixWorld)
}
updateProjectionMatrix() {
mat4.perspective(this.projectionMatrix, this.fov * Math.PI / 180, this.aspect, this.near, this.far)
mat4.invert(this.projectionMatrixInverse, this.projectionMatrix)
}
}
class Mesh extends Object3D {
constructor() {
super()
}
render() {
}
}
function mouseButtons(ev) {
if (typeof ev === 'object') {
if ('buttons' in ev) {
return ev.buttons
} else if ('which' in ev) {
var b = ev.which
if (b === 2) {
return 4
} else if (b === 3) {
return 2
} else if (b > 0) {
return 1 << (b - 1)
}
} else if ('button' in ev) {
var b = ev.button
if (b === 1) {
return 4
} else if (b === 2) {
return 2
} else if (b >= 0) {
return 1 << b
}
}
}
return 0
}
function lookAt() {
}
class OribtControl {
constructor(camera, domElement) {
this.domElement = domElement
/**@type {Camera}*/
this.camera = camera
let actionType = '',
vec2_start = vec2.create(),
vec2_end = vec2.create(),
vec2_offset = vec2.create(),
vec2_delta = vec2.create()
let pitch = 0 // 俯仰角
let yaw = 0 // 偏航角
let roll = 0 // 翻滚角
let onDown = (e) => {
this.domElement.addEventListener('pointermove', onMove, false)
this.domElement.addEventListener('pointerup', onUp, false)
if (e.buttons == 1) {
actionType = "rotate"
} else if (e.buttons == 2) {
actionType = 'pan'
}
vec2.set(vec2_start, e.clientX, e.clientY)
}
let up = vec3.sub([], this.camera.up, [1, 1, 1])
let onMove = (e) => {
e.preventDefault()
vec2.set(vec2_end, e.clientX, e.clientY)
vec2.sub(vec2_delta, vec2_start, vec2_end)
vec2.copy(vec2_start, vec2_end)
this.camera.position[0] += vec2_delta[0] * 0.01
this.camera.position[1] += vec2_delta[1] * 0.01
updateCamera()
}
let onUp = () => {
this.domElement.removeEventListener('pointermove', onMove, false)
this.domElement.removeEventListener('pointerup', onUp, false)
}
let onContextMenu = (e) => {
e.preventDefault()
}
let onWheel = (e) => {
}
let updateCamera = () => {
this.camera.updateMatrixWorld()
}
this.attachEvent = function () {
this.domElement.addEventListener('contextmenu', onContextMenu, false)
this.domElement.addEventListener('pointerdown', onDown, false)
this.domElement.addEventListener('wheel', onWheel, false)
}
this.detachEvent = function () {
this.domElement.removeEventListener('contextmenu', onContextMenu, false)
this.domElement.removeEventListener('pointerdown', onDown, false)
this.domElement.removeEventListener('wheel', onWheel, false)
}
this.attachEvent()
}
}
let imageResources = {}
function loadImage(name, url) {
return new Promise((resolve, reject) => {
let img = new Image()
requestCORSIfNotSameOrigin(img, url)
img.onload = () => {
let item = imageResources[name] = {
url: url,
image: img
}
resolve(item)
}
img.src = url
})
}
function loadResources(resources) {
return Promise.allSettled(resources.map(r => {
return loadImage(r.name, r.url)
}))
}
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url, window.location.href)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
let boxVertices = [
// 上 红
-1, 1, -1,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
// 下 绿
-1, -1, -1,
-1, -1, 1,
1, -1, 1,
1, -1, -1,
// 前 蓝
-1, 1, 1,
-1, -1, 1,
1, -1, 1,
1, 1, 1,
// 后 黄
- 1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, 1, -1,
// 左 青
-1, 1, -1,
-1, -1, -1,
-1, -1, 1,
-1, 1, 1,
// 右 品红
1, 1, -1,
1, -1, -1,
1, -1, 1,
1, 1, 1,
]
let boxIndices = [
// 上
0, 1, 2,
0, 3, 2,
// 下
4, 5, 6,
4, 7, 6,
// 前
8, 9, 10,
8, 11, 10,
// 后
12, 13, 14,
12, 15, 14,
// 左
16, 17, 18,
16, 19, 18,
// 右
20, 21, 22,
20, 23, 22
]
let boxColors = [
// 上 红
1.0, 0, 0,
1.0, 0, 0,
1.0, 0, 0,
1.0, 0, 0,
// 下 绿
0, 1.0, 0,
0, 1.0, 0,
0, 1.0, 0,
0, 1.0, 0,
// 前 蓝
0, 0, 1.0,
0, 0, 1.0,
0, 0, 1.0,
0, 0, 1.0,
// 后 黄
1.0, 1.0, 0,
1.0, 1.0, 0,
1.0, 1.0, 0,
1.0, 1.0, 0,
// 左 青
0, 1.0, 1.0,
0, 1.0, 1.0,
0, 1.0, 1.0,
0, 1.0, 1.0,
// 右 品红
1.0, 0, 1.0,
1.0, 0, 1.0,
1.0, 0, 1.0,
1.0, 0, 1.0
]
function generateFace(ctx, faceColor, textColor, text) {
const { width, height } = ctx.canvas;
ctx.clearRect(0, 0, width, height)
ctx.fillStyle = faceColor;
ctx.fillRect(0, 0, width, height);
ctx.font = `${width * 0.7}px sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = textColor;
ctx.fillText(text, width / 2, height / 2);
}
function generateCubeFaceTexture(width, height) {
// 获取二维上下文
/** @type {CanvasRenderingContext2D} */
const ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = width;
ctx.canvas.height = height;
const faceInfos = [
{ faceColor: '#F00', textColor: '#0FF', text: '+X', key: 'right' },
{ faceColor: '#FF0', textColor: '#00F', text: '-X', key: 'left' },
{ faceColor: '#0F0', textColor: '#F0F', text: '+Y', key: 'top' },
{ faceColor: '#0FF', textColor: '#F00', text: '-Y', key: 'bottom' },
{ faceColor: '#00F', textColor: '#FF0', text: '+Z', key: 'front' },
{ faceColor: '#F0F', textColor: '#0F0', text: '-Z', key: 'back' },
];
let imageDatas = {}
faceInfos.forEach((faceInfo) => {
const { faceColor, textColor, text } = faceInfo;
generateFace(ctx, faceColor, textColor, text);
let imageData = ctx.getImageData(0, 0, width, height)
// let buffer=new ArrayBuffer()
// let dataView=new DataView(buffer)
imageDatas[faceInfo.key] = imageData.data.slice()
});
return imageDatas
}
let BaseComponent = {
template: `<div ><div ref="container" :style="style"></div></div>`,
data() {
return {
width: 600,
height: 400,
devicePixelRatio: window.devicePixelRatio,
resources: [],
extensions: []
}
},
computed: {
style() {
return {
width: this.width + 'px',
height: this.height + 'px',
position: 'relative'
}
}
},
methods: {
init(regl) { },
onDown(e) {
this.$emit('down', e)
},
onMove(e) {
this.$emit('move', e)
},
onUp(e) {
this.$emit('up', e)
}
},
/**@type {createREGL.Regl}*/
regl: null,
mounted() {
var container = this.$refs.container;
var regl = this.regl = createREGL({
container: container,
pixelRatio: this.devicePixelRatio,
profile: true,
attributes: {
antialias: true,
stencil:true
},
extensions: [].concat(this.extensions)
})
// 加载资源
loadResources([{
name: 'texture',
url: '/assets/textures/uv_grid_opengl.jpg'
}, {
name: "texture2",
url: '/assets/pics/aya-touhou-teng-soldier.png'
}, {
name: "diamonds",
url: '/assets/sprites/diamonds32x24x5.png'
}].concat(this.resources)).then(img => {
this.init(regl)
})
window.addEventListener('mousedown', this.onDown)
window.addEventListener('mousemove', this.onMove)
window.addEventListener('mouseup', this.onUp)
},
destroyed() {
window.removeEventListener('mousedown', this.onDown)
window.removeEventListener('mousemove', this.onMove)
window.removeEventListener('mouseup', this.onUp)
this.regl.destroy()
}
}
addExample("绘制边框", function () {
return {
extends: BaseComponent,
data() { return {}; },
computed: {},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
}
},
mounted() {
var container = this.$refs.main;
}
}
})
addExample("2d坐标系统", function () {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 2d 左上角是原点(0,0)
// 3d 中间是原点(0,0)
// 平面投影,将2d屏幕坐标转换为裁剪坐标(-1,1)
// 将屏幕坐标投射为裁剪坐标
let projectMatrix = mat3.create()
mat3.projection(projectMatrix, width, height)
let position = [
[100, 100],
[200, 100],
[200, 200],
[100, 200]
]
let strokeRect = regl({
vert: `
attribute vec2 position;
uniform mat3 projectMatrix;
uniform mat3 modelMatrix;
void main(){
vec3 _position=projectMatrix* modelMatrix *vec3(position,1);
gl_Position=vec4(vec2(_position),0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
primitive: "line loop",
attributes: {
position: position
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
modelMatrix: regl.prop('modelMatrix'),
color: regl.prop('color')
},
count: position.length,
depth: {
enable: true,
mask: false
}
})
let modelMatrix = mat3.create()
let modelMatrix2 = mat3.create()
let skewX = -0.3, skewY = 0
let tanX = Math.tan(skewX), tanY = Math.tan(skewY)
let tanMatrix2 = mat3.fromValues(
1, tanX, 0,
tanY, 1, 0,
0, 0, 1
)
mat3.transpose(tanMatrix2, tanMatrix2)
mat3.translate(modelMatrix2, modelMatrix2, [200, 200])
mat3.multiply(modelMatrix2, modelMatrix2, tanMatrix2)
mat3.translate(modelMatrix2, modelMatrix2, [-200, -200])
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
// mat3.translate(modelMatrix,modelMatrix,[100,100])
// mat3.rotate(modelMatrix,modelMatrix,0.1)
// mat3.translate(modelMatrix,modelMatrix,[-100,-100])
strokeRect([{
projectMatrix: projectMatrix,
modelMatrix: modelMatrix,
color: [1, 0, 0, 1]
},
{
projectMatrix: projectMatrix,
modelMatrix: modelMatrix2,
color: [1, 1, 0, 1]
}])
})
}
}
}
})
addExample("webgl仿2dCanvas", function () {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
class GLCanvasPath {
static uid = 0
constructor() {
this.uid = 'path_' + (GLCanvasPath.uid++)
this.pathData = new Map()
this.isFill = false
}
moveTo(x, y) {
this.pathData.set('moveTo', [x, y])
}
lineTo(x, y) {
this.pathData.set('lineTo', [x, y])
}
rect(x, y, w, h) {
this.pathData.set('rect', [x, y, w, h])
}
toFillPath() {
let paths = []
this.pathData.forEach((d, name) => {
if (name === 'rect') {
paths.push(d[0], d[1])
paths.push(d[0] + d[2], d[1])
paths.push(d[0] + d[2], d[1] + d[3])
paths.push(d[0], d[1])
paths.push(d[0], d[1] + d[3])
paths.push(d[0] + d[2], d[1] + d[3])
}
})
return new Float32Array(paths)
}
toStrokePath(lineWidth) {
let paths = []
this.pathData.forEach((d, name) => {
if (name === 'rect') {
for (let l = 1; l <= lineWidth; l++) {
let x = d[0] - l, y = d[1] - l
let w = d[2] + l * 2, h = d[3] + l * 2
paths.push(x, y)
paths.push(x + w, y)
paths.push(x + w, y + h)
paths.push(x, y + w)
paths.push(x, y)
}
}
})
return new Float32Array(paths)
}
}
class GLCanvas {
/**
* @type {createREGL.Regl} regl
*/
regl = null
constructor(regl) {
this.projectMatrix = mat3.create()
this.modelMatrix = mat3.create()
this.matrixStack = []
this.regl = regl
this._gldraw = null
this.reset()
}
updateProjectMatrix() {
mat3.projection(this.projectMatrix, this.regl._gl.drawingBufferWidth, this.regl._gl.drawingBufferHeight)
}
initGLDraw() {
this.updateProjectMatrix()
let regl = this.regl
this._gldraw = regl({
vert: `
attribute vec2 position;
uniform mat3 projectMatrix;
uniform mat3 modelMatrix;
void main(){
vec3 _position=projectMatrix* modelMatrix *vec3(position,1);
gl_Position=vec4(vec2(_position),0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
primitive: regl.prop('primitive'),
attributes: {
position: regl.prop('position')
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
modelMatrix: regl.prop('modelMatrix'),
color: regl.prop('color')
},
count: (context, props, bachId) => {
console.log('props.position.length', props.position.length / 2)
return props.position.length / 2
},
blend: {
enable: true,
func: {
srcRGB: 'src alpha',
srcAlpha: 1,
dstRGB: 'one minus src alpha',
dstAlpha: 1
},
equation: {
rgb: 'add',
alpha: 'add'
},
color: [0, 0, 0, 0]
}
})
}
get gldraw() {
if (!this._gldraw) {
this.initGLDraw()
}
return this._gldraw
}
set fillStyle(color) {
this._fillStyle = color
}
get fillStyle() {
return this._fillStyle
}
set strokeStyle(color) {
this._strokeStyle = color
}
get strokeStyle() {
return this._strokeStyle
}
get currentMatrix() {
return this.matrixStack[this.matrixStack.length - 1]
}
save() {
this.matrixStack.push(mat3.clone(this.currentMatrix))
return this
}
restore() {
this.matrixStack.pop()
if (this.matrixStack.length <= 0) {
this.matrixStack[0] = mat3.create()
}
return this
}
transform(a, b, c, d, tx, ty) {
let newMatrix = mat3.create()
mat3.set(newMatrix, a, b, 0, c, d, 0, tx, ty, 1)
mat3.multiply(newMatrix, this.currentMatrix, newMatrix)
this.matrixStack.push(newMatrix)
}
setTransform(a, b, c, d, tx, ty) {
mat3.set(this.currentMatrix, a, b, 0, c, d, 0, tx, ty, 1)
}
translate(x, y) {
mat3.translate(this.currentMatrix, this.currentMatrix, [x, y])
}
scale(x, y) {
mat3.scale(this.currentMatrix, this.currentMatrix, [x, y])
}
rotate(radian) {
mat3.rotate(this.currentMatrix, this.currentMatrix, radian)
}
resetMatrix() {
this.matrixStack.length = 0
this.matrixStack.push(mat3.create())
}
reset() {
this.currentPath = new GLCanvasPath()
this.resetMatrix()
this.drawOptions = Object.create(null)
this._fillStyle = [0, 0, 0, 1]
this._strokeStyle = [0, 0, 0, 1]
this.lineWidth = 1
this.clearColor([1, 1, 1, 1])
}
beginPath() {
this.currentPath = new GLCanvasPath()
}
rect(x, y, w, h) {
this.currentPath.rect(x, y, w, h)
}
fill() {
if (this.currentPath.isFill) {
return
}
this.currentPath.isFill = true
this.drawOptions.color = this.fillStyle
this.drawOptions.position = this.currentPath.toFillPath()
this.drawOptions.primitive = glsl.primitive.triangles
this.draw()
}
stroke() {
if (this.currentPath.isStroke) {
return
}
this.currentPath.isStroke = true
this.drawOptions.color = this.strokeStyle
this.drawOptions.position = this.currentPath.toStrokePath(this.lineWidth)
this.drawOptions.primitive = glsl.primitive.lineStrip
this.draw()
}
clearColor(color) {
this._clearColor = color
}
clear() {
this.regl.clear({
color: this._clearColor,
depth: 1
})
this.reset()
}
draw() {
this.drawOptions.projectMatrix = this.projectMatrix
this.drawOptions.modelMatrix = this.currentMatrix
this.drawOptions.projectMatrix = this.projectMatrix
this.gldraw(this.drawOptions)
}
}
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height,
devicePixelRatio: 1
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let canvas = new GLCanvas(regl)
canvas.clearColor([0.4, 0.4, 0.4, 1])
canvas.clear()
canvas.save()
canvas.translate(0, 100)
canvas.rect(100, 100, 100, 100)
canvas.fillStyle = [1, 1, 1, 1]
canvas.fill()
canvas.lineWidth = 5
canvas.strokeStyle = [0, 0, 0, 1]
canvas.stroke()
canvas.restore()
canvas.translate(150, 200)
canvas.beginPath()
canvas.rect(0, 0, 100, 100)
canvas.fillStyle = [1, 0, 1, 1]
canvas.fill()
canvas.translate(0, 100)
canvas.beginPath()
canvas.rect(320, 100, 100, 100)
canvas.fillStyle = [1, 1, 0, 1]
canvas.fill()
canvas.setTransform(1, 0, 0, 1, 0, 0)
canvas.beginPath()
canvas.rect(430, 100, 100, 100)
canvas.fillStyle = [0, 1, 0, 1]
canvas.fill()
}
}
}
})
addExample("3d坐标系统", function ({ gui }) {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 将屏幕坐标投射为裁剪坐标
let projectMatrix = mat4.create()
// 将0居中,(-250,250)屏幕坐标转为(-1,1)
// 正交投影z的变化不影响z的深度
// mat4.ortho(projectMatrix, width / -2, width / 2, height / 2, height / -2, 0, 500)
mat4.perspective(projectMatrix, 50 * Math.PI / 180, width / height, 0.1, 1000)
// 视图矩阵
let viewMatrix = mat4.create()
let cameraPosition = vec3.fromValues(0, 2, 5)// 相机所在位置
let lookTargetPosition = vec3.fromValues(0, 0, 0) // 相机拍摄目标
let up = vec3.fromValues(0, 1, 0) // 相机自身朝向
mat4.lookAt(viewMatrix, cameraPosition, lookTargetPosition, up)
// mat4.invert(viewMatrix,viewMatrix)
let position = [
// 后
[-1, -1, -1], //左下 0
[1, -1, -1], // 右下 1
[1, 1, -1], // 右上 2
[-1, 1, -1], // 左上 3
// 前
[-1, -1, 1], //左下 4
[1, -1, 1],// 右下 5
[1, 1, 1],// 右上 6
[-1, 1, 1],// 左上 7
]
let elements = [
// back
0, 1, 2,
0, 3, 2,
// front
4, 5, 6,
4, 7, 6,
// left
0, 4, 7,
0, 3, 7,
//right
5, 1, 2,
5, 6, 2,
//top
7, 6, 2,
7, 3, 2,
// bottom
4, 5, 2,
4, 3, 2
]
let strokeRect = regl({
vert: `
attribute vec3 position;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
attributes: {
position: position
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
color: regl.prop('color')
},
elements: regl.elements({
primitive: "triangles",
data: elements,
// count:elements.length,
type: "uint8"
}),
// primitive: "triangles",
//count: position.length,
})
let modelMatrix = mat4.create()
let modelQuat = quat.create()
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
quat.rotateY(modelQuat, modelQuat, 0.01)
// quat.rotateX(modelQuat,modelQuat,0.01)
mat4.fromQuat(modelMatrix, modelQuat)
strokeRect({
projectMatrix: projectMatrix,
viewMatrix: viewMatrix,
modelMatrix: modelMatrix,
color: [1, 0, 0, 1]
})
})
}
}
}
})
addExample("3d 顶点着色", function ({ gui }) {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 将屏幕坐标投射为裁剪坐标
let projectMatrix = mat4.create()
// 将0居中,(-250,250)屏幕坐标转为(-1,1)
// 正交投影z的变化不影响z的深度
// mat4.ortho(projectMatrix, width / -2, width / 2, height / 2, height / -2, 0, 500)
mat4.perspective(projectMatrix, 50 * Math.PI / 180, width / height, 0.1, 1000)
// 视图矩阵
let viewMatrix = mat4.create()
let cameraPosition = vec3.fromValues(0, 2, 5)// 相机所在位置
let lookTargetPosition = vec3.fromValues(0, 0, 0) // 相机拍摄目标
let up = vec3.fromValues(0, 1, 0) // 相机自身朝向
mat4.lookAt(viewMatrix, cameraPosition, lookTargetPosition, up)
// mat4.invert(viewMatrix,viewMatrix)
let vertexs = [
// 上 红
-1, 1, -1, 1.0, 0, 0,
-1, 1, 1, 1.0, 0, 0,
1, 1, 1, 1.0, 0, 0,
1, 1, -1, 1.0, 0, 0,
// 下 绿
-1, -1, -1, 0, 1.0, 0,
-1, -1, 1, 0, 1.0, 0,
1, -1, 1, 0, 1.0, 0,
1, -1, -1, 0, 1.0, 0,
// 前 蓝
-1, 1, 1, 0, 0, 1.0,
-1, -1, 1, 0, 0, 1.0,
1, -1, 1, 0, 0, 1.0,
1, 1, 1, 0, 0, 1.0,
// 后 黄
- 1, 1, -1, 1.0, 1.0, 0,
-1, -1, -1, 1.0, 1.0, 0,
1, -1, -1, 1.0, 1.0, 0,
1, 1, -1, 1.0, 1.0, 0,
// 左 青
-1, 1, -1, 0, 1.0, 1.0,
-1, -1, -1, 0, 1.0, 1.0,
-1, -1, 1, 0, 1.0, 1.0,
-1, 1, 1, 0, 1.0, 1.0,
// 右 品红
1, 1, -1, 1.0, 0, 1.0,
1, -1, -1, 1.0, 0, 1.0,
1, -1, 1, 1.0, 0, 1.0,
1, 1, 1, 1.0, 0, 1.0
]
let vertexBuffer = regl.buffer({
data: vertexs,
type: "float",
usage: "static"
})
let strokeRect = regl({
vert: `
attribute vec3 position;
attribute vec3 color;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
varying vec3 v_color;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
v_color=color;
}
`,
frag: `
precision mediump float;
varying vec3 v_color;
void main(){
gl_FragColor= vec4(v_color,1.0);
}
`,
attributes: {
position: {
buffer: vertexBuffer,
size: 3,
offset: 0,
stride: 6 * Float32Array.BYTES_PER_ELEMENT,
type: "float"
},
color: {
buffer: vertexBuffer,
size: 3,
offset: 3 * Float32Array.BYTES_PER_ELEMENT,
stride: 6 * Float32Array.BYTES_PER_ELEMENT,
type: "float"
}
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
},
elements: regl.elements({
primitive: "triangles",
data: boxIndices,
// count:elements.length,
type: "uint16"
})
// primitive: "triangles",
//count: position.length,
})
let modelMatrix = mat4.create()
let modelQuat = quat.create()
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
quat.rotateY(modelQuat, modelQuat, 0.01)
// quat.rotateX(modelQuat,modelQuat,0.01)
mat4.fromQuat(modelMatrix, modelQuat)
strokeRect({
projectMatrix: projectMatrix,
viewMatrix: viewMatrix,
modelMatrix: modelMatrix
})
})
}
}
}
})
addExample("3d 顶点单独着色", function ({ gui }) {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 将屏幕坐标投射为裁剪坐标
let projectMatrix = mat4.create()
// 将0居中,(-250,250)屏幕坐标转为(-1,1)
// 正交投影z的变化不影响z的深度
// mat4.ortho(projectMatrix, width / -2, width / 2, height / 2, height / -2, 0, 500)
mat4.perspective(projectMatrix, 50 * Math.PI / 180, width / height, 0.1, 1000)
// 视图矩阵
let viewMatrix = mat4.create()
let cameraPosition = vec3.fromValues(0, 2, 5)// 相机所在位置
let lookTargetPosition = vec3.fromValues(0, 0, 0) // 相机拍摄目标
let up = vec3.fromValues(0, 1, 0) // 相机自身朝向
mat4.lookAt(viewMatrix, cameraPosition, lookTargetPosition, up)
// mat4.invert(viewMatrix,viewMatrix)
var colors = [
// 上 红
1.0, 0, 0,
1.0, 0, 0,
1.0, 0, 0,
1.0, 0, 0,
// 下 绿
0, 1.0, 0,
0, 1.0, 0,
0, 1.0, 0,
0, 1.0, 0,
// 前 蓝
0, 0, 1.0,
0, 0, 1.0,
0, 0, 1.0,
0, 0, 1.0,
// 后 黄
1.0, 1.0, 0,
1.0, 1.0, 0,
1.0, 1.0, 0,
1.0, 1.0, 0,
// 左 青
0, 1.0, 1.0,
0, 1.0, 1.0,
0, 1.0, 1.0,
0, 1.0, 1.0,
// 右 品红
1.0, 0, 1.0,
1.0, 0, 1.0,
1.0, 0, 1.0,
1.0, 0, 1.0
]
let strokeRect = regl({
vert: `
attribute vec3 position;
attribute vec3 color;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
varying vec3 v_color;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
v_color=color;
}
`,
frag: `
precision mediump float;
varying vec3 v_color;
void main(){
gl_FragColor= vec4(v_color,1.0);
}
`,
attributes: {
position: {
buffer: boxVertices,
size: 3,
offset: 0,
stride: 0,
type: "float"
},
color: {
buffer: colors,
size: 3,
offset: 0,
stride: 0,
type: "float"
}
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
},
elements: regl.elements({
primitive: "triangles",
data: boxIndices,
// count:elements.length,
type: "uint16"
})
// primitive: "triangles",
//count: position.length,
})
let modelMatrix = mat4.create()
let modelQuat = quat.create()
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
// quat.rotateY(modelQuat, modelQuat, 0.01)
// quat.rotateX(modelQuat,modelQuat,0.01)
// mat4.fromQuat(modelMatrix, modelQuat)
strokeRect({
projectMatrix: projectMatrix,
viewMatrix: viewMatrix,
modelMatrix: modelMatrix
})
})
}
}
}
})
addExample("3d 纹理贴图", function ({ gui }) {
// 图片坐标:是左上角(0,0)开始,右下角(1,1)
// 纹理坐标:是左上角(0,1),右下角(1,0)
// 在设置时,坐标
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 将屏幕坐标投射为裁剪坐标
let projectMatrix = mat4.create()
mat4.perspective(projectMatrix, 50 * Math.PI / 180, width / height, 0.1, 1000)
// 视图矩阵
let viewMatrix = mat4.create()
let cameraPosition = vec3.fromValues(0, 0, 5)// 相机所在位置
let lookTargetPosition = vec3.fromValues(0, 0, 0) // 相机拍摄目标
let up = vec3.fromValues(0, 1, 0) // 相机自身朝向
mat4.lookAt(viewMatrix, cameraPosition, lookTargetPosition, up)
// mat4.invert(viewMatrix,viewMatrix)
/**
(0,0) --> (-1,1,0)
(1,0) --> (1,1,0)
(1,1) --> (1,-1,0)
(0,1) --> (-1,-1,0)
其映射关系为
u = (x + 1) / 2
v = (1 - y) / 2
默认:纹理坐标是网页2d坐标:左上角(0,0) 右上角(1,0) 左下角(0,1) 右下角(1,1)
通过 flipY 翻转Y轴:左上角(0,1) 右上角(1,1) 左下角(0,0) 右下角(1,0)
目前下面定义未按上面一一对应,如果按上面对应图片是倒的.原因未知,需要flipY:true
下面是按照网页2d坐标来映射
(0,0) --> (-1,1,0)
(1,0) --> (1,1,0)
(1,1) --> (1,-1,0)
(0,1) --> (-1,-1,0)
*
* */
var uv = [
// 上 红
0, 0,
0, 1,
1, 1,
1, 0,
// 下 绿
0, 0,
0, 1,
1, 1,
1, 0,
// 前 蓝
0, 1,
0, 0,
1, 0,
1, 1,
// 后 黄
0, 0,
0, 1,
1, 1,
1, 0,
// 左 青
0, 0,
0, 1,
1, 1,
1, 0,
// 右 品红
0, 0,
0, 1,
1, 1,
1, 0,
]
var texture = regl.texture({
data: imageResources.texture.image,
wrapS: "clamp",
wrapT: "clamp",
// min:"nearest",
// format:"rgb",
// type:"uint8",
// flipY:true
// mag:"nearest"
})
let strokeRect = regl({
vert: `
attribute vec3 position;
attribute vec2 a_texCoord;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
varying vec2 v_texCoord;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
// 将纹理坐标传给片断着色器
// GPU会在点之间进行插值
v_texCoord=a_texCoord;
}
`,
frag: `
precision mediump float;
// 纹理
uniform sampler2D u_image;
// 从顶点着色器传入的纹理坐标
varying vec2 v_texCoord;
void main(){
// 翻转y轴
vec2 flipY= vec2(v_texCoord.x,1.0-v_texCoord.y);
// 在纹理上寻找对应颜色值
gl_FragColor = texture2D(u_image, flipY);
}
`,
attributes: {
position: {
buffer: boxVertices,
size: 3,
offset: 0,
stride: 0,
type: "float"
},
a_texCoord: {
buffer: uv,
size: 2,
offset: 0,
stride: 0,
type: "float"
}
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
u_image: texture
},
elements: regl.elements({
primitive: "triangles",
data: boxIndices,
// count:elements.length,
type: "uint16"
})
// primitive: "triangles",
//count: position.length,
})
let modelMatrix = mat4.create()
let modelQuat = quat.create()
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
// quat.rotateY(modelQuat, modelQuat, 0.01)
// quat.rotateX(modelQuat,modelQuat,0.01)
// mat4.fromQuat(modelMatrix, modelQuat)
strokeRect({
projectMatrix: projectMatrix,
viewMatrix: viewMatrix,
modelMatrix: modelMatrix
})
})
}
}
}
})
addExample("3d 立体纹理贴图", function ({ gui }) {
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
// 将屏幕坐标投射为裁剪坐标
let camera = new Camera(45, width / height, 0.001, 1000)
camera.setPoisition(0, 2, 5)
camera.lookAt(vec3.fromValues(0, 0, 0))
let oribt = new OribtControl(camera, regl._gl.canvas)
let textureImageData = generateCubeFaceTexture(500, 500)
var texture = regl.cube({
width: 500,
height: 500,
wrap: "clamp",
//min:"linear mipmap linear",
faces: [
textureImageData.right, textureImageData.left,
textureImageData.top, textureImageData.bottom,
textureImageData.front, textureImageData.back
]
})
let strokeRect = regl({
vert: `
attribute vec3 position;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
varying vec3 v_normal;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
// 传递法向量。因为位置是以几何中心为原点的
// 我们可以直接传递位置
v_normal = normalize(position.xyz);
}
`,
frag: `
precision mediump float;
// 纹理
uniform samplerCube u_texture;
// 从顶点着色器传入的纹理
varying vec3 v_normal;
void main(){
// 在纹理上寻找对应颜色值
gl_FragColor = textureCube(u_texture, normalize(v_normal));
}
`,
attributes: {
position: {
buffer: boxVertices,
size: 3,
offset: 0,
stride: 0,
type: "float"
}
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
u_texture: texture
},
elements: regl.elements({
primitive: "triangles",
data: boxIndices,
// count:elements.length,
type: "uint16"
})
// primitive: "triangles",
//count: position.length,
})
let obj = new Object3D()
addGuiScheme(gui, {
controls: {
vec3: (api) => {
api.gui.add(api.value, 0, ...api.scheme.params).name(api.name + '.x').onChange(api.onChange)
api.gui.add(api.value, 1, ...api.scheme.params).name(api.name + '.y').onChange(api.onChange)
api.gui.add(api.value, 2, ...api.scheme.params).name(api.name + '.z').onChange(api.onChange)
}
},
source: {
'position': camera.position,
'direction': () => {
console.log('getWorldDirection', camera.getWorldDirection())
},
'direction2': () => {
console.log('getWorldDirection2', camera.getWorldDirection2())
}
},
schemes: {
'position': {
type: "vec3",
params: [-10, 10, 0.1]
}
},
onChange(d) {
// camera.setRotation(d['rotate.x'],d['rotate.y'],d['rotate.z'])
camera.lookAt(vec3.fromValues(0, 0, 0))
}
})
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
strokeRect({
projectMatrix: camera.projectionMatrix,
viewMatrix: camera.matrixWorldInvert,
modelMatrix: obj.matrixWorld
})
})
}
}
}
})
addExample("3d 交互", function ({ gui }) {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let strokeRect = regl({
vert: `
attribute vec3 position;
attribute vec3 color;
uniform mat4 projectMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
varying vec3 v_color;
void main(){
vec4 _position=projectMatrix*viewMatrix* modelMatrix * vec4(position,1.0);
gl_Position=_position;
v_color=color;
}
`,
frag: `
precision mediump float;
varying vec3 v_color;
void main(){
gl_FragColor= vec4(v_color,1.0);
}
`,
attributes: {
position: {
buffer: boxVertices,
size: 3,
offset: 0,
stride: 0,
type: "float"
},
color: {
buffer: boxColors,
size: 3,
offset: 0,
stride: 0,
type: "float"
}
},
uniforms: {
projectMatrix: regl.prop('projectMatrix'),
viewMatrix: regl.prop('viewMatrix'),
modelMatrix: regl.prop('modelMatrix'),
},
elements: regl.elements({
primitive: "triangles",
data: boxIndices,
// count:elements.length,
type: "uint16"
})
// primitive: "triangles",
//count: position.length,
})
let camera = new Camera(45, width / height, 0.001, 1000)
camera.setPoisition(0, 2, 5)
camera.lookAt(vec3.fromValues(0, 0, 0))
let box = new Object3D()
box.updateMatrixWorld()
let ray = new Ray3D.Ray()
this.$on('down', (e) => {
let rayOrigin = vec3.create(), // 相机在世界空间的坐标
rayPoint = vec3.create(),// 从鼠标位置裁剪坐标转换为世界空间坐标(与相机在同一个空间)
rayDirection = vec3.create() // rayPoint-rayOrigin 得到归一化,的方向向量
let rect = regl._gl.canvas.getBoundingClientRect()
// 获取相机世界位置
// vec3.transformMat4(rayOrigin,rayOrigin, camera.matrixWorld)
mat4.getTranslation(rayOrigin, camera.matrixWorld)
// 将屏幕坐标转换为gl裁剪坐标
let x = (e.clientX - rect.left) / rect.width * 2 - 1
let y = -(e.clientY - rect.top) / rect.height * 2 + 1
// 设置鼠标世界坐标
vec3.set(rayPoint, x, y, 0.5)
// var vp = mat4.multiply([], camera.projectionMatrix, camera.matrixWorldInvert)
// var invVp = mat4.invert([], vp)
// console.log('fff',vec3.transformMat4([],rayPoint,invVp))
// console.log('fff2',vec3.transformMat4([],vec3.transformMat4([],rayPoint,camera.projectionMatrixInverse),camera.matrixWorld))
// 将投影截剪坐标转为世界坐标,
vec3.transformMat4(rayPoint, rayPoint, camera.projectionMatrixInverse)
// 相对相机世界坐标
vec3.transformMat4(rayPoint, rayPoint, camera.matrixWorld)
// 得到相对原点向量
vec3.sub(rayDirection, rayPoint, rayOrigin)
// 得到单位向量,方向
vec3.normalize(rayDirection, rayDirection)
// console.log('rayPoint', rayPoint, 'rayDirection', rayDirection, ',rayOrigin:', rayOrigin)
// console.log('camera-direction', camera.getWorldDirection(vec3.create()))
ray.origin = rayOrigin
ray.direction = rayDirection
let a, b, c, p1 = vec3.create(), p2 = vec3.create(), p3 = vec3.create(), triangle = new Array(3)
let triangleInterserect
for (let i = 0; i < boxIndices.length; i += 3) {
a = boxIndices[i] * 3
b = boxIndices[i + 1] * 3
c = boxIndices[i + 2] * 3
vec3.set(p1, boxVertices[a], boxVertices[a + 1], boxVertices[a + 2])
vec3.set(p2, boxVertices[b], boxVertices[b + 1], boxVertices[b + 2])
vec3.set(p3, boxVertices[c], boxVertices[c + 1], boxVertices[c + 2])
vec3.transformMat4(p1, p1, box.matrixWorld)
vec3.transformMat4(p2, p2, box.matrixWorld)
vec3.transformMat4(p3, p3, box.matrixWorld)
triangle[0] = p1
triangle[1] = p2
triangle[2] = p3
// triangleInterserect=Ray3D.intersectRayTriangle([],rayPoint,rayDirection,triangle)
triangleInterserect = ray.intersectsTriangle(triangle)
if (triangleInterserect != null) {
console.log('点击')
break
}
}
})
this.$on('move', (e) => {
})
let ticker = regl.frame(() => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
// quat.rotateY(modelQuat, modelQuat, 0.01)
// quat.rotateX(modelQuat,modelQuat,0.01)
// mat4.fromQuat(modelMatrix, modelQuat)
strokeRect({
projectMatrix: camera.projectionMatrix,
viewMatrix: camera.matrixWorldInvert,
modelMatrix: box.matrixWorld
})
})
}
}
}
})
addExample("3d 天空盒", function ({ gui }) {
// 2d投影矩阵,把世界坐标转换为屏幕
let width = 500, height = 500
const faceInfos = [
{
// target: gl.TEXTURE_CUBE_MAP_POSITIVE_X,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/pos-x.jpg',
},
{
// target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/neg-x.jpg',
},
{
// target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/pos-y.jpg',
},
{
// target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/neg-y.jpg',
},
{
// target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/pos-z.jpg',
},
{
// target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
url: 'http://localhost:8349/webgl/resources/images/computer-history-museum/neg-z.jpg',
},
].map(d => {
let index = d.url.lastIndexOf('/')
return {
name: d.url,
url: d.url
}
});
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height,
resources: faceInfos
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let cubeTexture = regl.cube({
width: 512,
height: 512,
faces: faceInfos.map(d => imageResources[d.name].image),
format: "rgba",
type: "uint8",
mipmap: "nice",
min: "linear mipmap linear",
})
let drawEnvmap = regl({
vert: `
attribute vec4 a_position;
varying vec4 v_position;
void main() {
v_position = a_position;
gl_Position = a_position;
gl_Position.z = 1.0;
}
`,
frag: `
precision mediump float;
uniform samplerCube u_skybox;
uniform mat4 projectionMatrixInverse;
uniform mat4 cameraWorldMatrix;
uniform mat4 u_viewDirectionProjectionInverse;
varying vec4 v_position;
void main() {
vec4 t = u_viewDirectionProjectionInverse * v_position;
gl_FragColor =textureCube(u_skybox, normalize(t.xyz / t.w));
// gl_FragColor=vec4(1.0,0,0,1.0);
}
`,
attributes: {
a_position: {
buffer: regl.buffer({
data: [
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
],
usage: "static",
type: "float"
}),
offset: 0,
stride: 0,
normalized: false,
size: 2,
type: "float"
}
},
uniforms: {
u_skybox: cubeTexture,
u_viewDirectionProjectionInverse: regl.prop('u_viewDirectionProjectionInverse')
},
primitive: "triangles",
count: 6,
depth: {
enable: true,
func: 'lequal'
}
})
let envmap = new Object3D()
envmap.draw = drawEnvmap
let camera = new Camera(60, width / height, 0.001, 1000)
camera.lookAt(vec3.fromValues(0, 0, 0))
var viewMatrix = mat4.create()
var viewDirectionProjectionMatrix = mat4.create()
var viewDirectionProjectionInverseMatrix = mat4.create()
let ticker = regl.frame(({ time }) => {
regl.clear({
color: [0, 0, 0, 1],
depth: 1
})
camera.position[0] = Math.cos(time * .1)
camera.position[1] = 0
camera.position[2] = Math.sin(time * .1)
camera.lookAt(vec3.fromValues(0, 0, 0))
// camera.updateMatrix()
// 获取相机视图矩阵
mat4.copy(viewMatrix, camera.matrixWorldInvert)
// mat4.invert(viewMatrix,viewMatrix)
// 我们只关心方向所以清除移动的部分
viewMatrix[12] = 0
viewMatrix[13] = 0
viewMatrix[14] = 0
mat4.mul(viewDirectionProjectionMatrix, camera.projectionMatrix, viewMatrix)
mat4.invert(viewDirectionProjectionInverseMatrix, viewDirectionProjectionMatrix)
envmap.draw({
u_viewDirectionProjectionInverse: viewDirectionProjectionInverseMatrix,
// viewMatrix:camera.viewMatrix
})
// ticker.cancel()
})
}
}
}
})
addExample("drawimage", function () {
return {
template: `<div><div ref="main"></div></div>`,
extends: BaseComponent,
data() { return {}; },
computed: {},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let imageTexture = regl.texture({
width: 160,
height: 24,
data: imageResources.diamonds.image,
format: "rgba",
wrap: "clamp",
mag: "linear",
type: "uint8",
// colorSpace:"browser",
// premultiplyAlpha:false
flipY: true
})
let buildRect = function (x, y, width, height) {
return [
x, y,
x + width, y,
x, y + height,
x, y + height,
x + width, y,
x + width, y + height
]
}
let dragRect = regl({
context: {
projection: function (context) {
return mat3.projection([], context.viewportWidth, context.viewportHeight)
}
},
vert: `
precision mediump float;
attribute vec2 position;
uniform mat3 projection;
void main(){
vec3 pos=projection*vec3(position,1.0);
gl_Position=vec4(pos.xy,0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main () {
gl_FragColor = color;
}`,
attributes: {
position: buildRect(100, 100, 160, 24)
},
uniforms: {
projection: regl.context('projection'),
color: [1.0, 0, 0, 1]
},
count: 6,
primitive: "triangles"
})
let dragImage = regl({
context: {
projection: function (context) {
return mat3.projection([], context.viewportWidth, context.viewportHeight)
}
},
vert: `
precision mediump float;
attribute vec2 position;
attribute vec2 auv;
uniform mat3 projection;
varying vec2 uv;
void main(){
vec3 pos=projection*vec3(position,1.0);
gl_Position=vec4(pos.xy,0,1.0);
uv=auv;
}
`,
frag: `
precision mediump float;
uniform sampler2D texture;
varying vec2 uv;
void main () {
gl_FragColor = texture2D(texture, uv);
}`,
attributes: {
position: buildRect(100, 100, 160, 24),
auv: [
0, 1,
1, 1,
0, 0,
0, 0,
1, 1,
1, 0
]
},
uniforms: {
projection: regl.context('projection'),
texture: imageTexture
},
blend: {
enable: true,
func: {
src: "src alpha",
dst: "one minus src alpha"
}
},
count: 6,
primitive: "triangles"
})
regl.clear({
depth: 1,
color: [0, 0, 0, 1.0],
stencil: 1
})
// dragRect()
dragImage({})
}
},
mounted() {
var container = this.$refs.main;
}
}
})
addExample("摄像机", function () {
function createCameraBufferInfo(scale = 1) {
// 首先,让我们添加一个立方体。它的范围是 1 到 3,
// 因为相机看向的是 -Z 方向,所以我们想要相机在 Z = 0 处开始。
// 我们会把一个圆锥放到该立方体的前面,
// 且该圆锥的开口方向朝 -Z 方向。
const positions = [
-1, -1, 1, // 立方体的顶点
1, -1, 1,
-1, 1, 1,
1, 1, 1,
-1, -1, 3,
1, -1, 3,
-1, 1, 3,
1, 1, 3,
0, 0, 1, // 圆锥的尖头
];
const indices = [
0, 1, 1, 3, 3, 2, 2, 0, // 立方体的索引
4, 5, 5, 7, 7, 6, 6, 4,
0, 4, 1, 5, 3, 7, 2, 6,
];
// 添加圆锥的片段
const numSegments = 6;
const coneBaseIndex = positions.length / 3;
const coneTipIndex = coneBaseIndex - 1;
for (let i = 0; i < numSegments; ++i) {
const u = i / numSegments;
const angle = u * Math.PI * 2;
const x = Math.cos(angle);
const y = Math.sin(angle);
positions.push(x, y, 0);
// 从圆锥尖头到圆锥边缘的线段
indices.push(coneTipIndex, coneBaseIndex + i);
// 从圆锥边缘一点到圆锥边缘下一点的线段
indices.push(coneBaseIndex + i, coneBaseIndex + (i + 1) % numSegments);
}
positions.forEach((v, ndx) => {
positions[ndx] *= scale;
});
return { positions, indices }
}
return {
template: `<div><div ref="main"></div></div>`,
extends: BaseComponent,
data() { return {}; },
computed: {},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let box = new THREE.BoxBufferGeometry(1, 1, 1)
let position = box.getAttribute('position').array
let elements = box.getIndex().array
let { positions: cameraPositions, indices: cameraIndices } = createCameraBufferInfo(1)
let drawCamera = regl({
vert: `
precision mediump float;
attribute vec3 position;
void main(){
gl_Position=vec4(position,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main () {
gl_FragColor = color;
}`,
attributes: {
position: cameraPositions,
},
uniforms: {
color: [1.0, 0, 0, 1]
},
elements: regl.elements({
primitive: "lines",
data: new Uint16Array(cameraIndices),
// length:position.length,
// count:elements.length,
type: "uint16"
}),
// count:position.length
})
regl.clear({
depth: 1,
color: [0, 0, 0, 1.0],
stencil: 1
})
drawCamera()
}
},
mounted() {
var container = this.$refs.main;
}
}
})
addExample("three", function () {
return {
template: `<div><div ref="main"></div></div>`,
data() { return {}; },
computed: {},
methods: {},
mounted() {
var container = this.$refs.main;
let renderer = new THREE.WebGLRenderer({
antialias: true
})
container.appendChild(renderer.domElement)
renderer.setSize(500, 500)
renderer.setClearColor(0x000000)
renderer.clearColor()
let camera = new THREE.PerspectiveCamera(50, 1, 0.001, 1000)
camera.position.set(0, 1, 2)
camera.lookAt(0, 0, 0)
let geometry = new THREE.BoxBufferGeometry(1, 1, 1)
let box = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: 0xff0000
}))
let scene = new THREE.Scene()
scene.add(box)
renderer.render(scene, camera)
}
}
})
addExample("callface", function () {
let width = 500, height = 500
return {
extends: BaseComponent,
data() {
return {
width: width,
height: height
}
},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let projectMatrix = mat3.create()
mat3.projection(projectMatrix, width, height)
const modelMatrix = mat3.create()
const createRect = (x, y, w, h) => {
// 顺时针,反面
let clockwise = new Float32Array([
x, y,
x, y + h,
x + w, y,
])
// 逆时针,正面
let inverse = new Float32Array([
x + w, y + h,
x, y + h,
x + w, y])
return {
clockwise,
inverse
}
}
let { clockwise, inverse } = createRect(100, 100, 100, 100)
let fillRectScope = regl({
vert: `
attribute vec2 position;
uniform mat3 projectMatrix;
uniform mat3 modelMatrix;
void main(){
vec3 _position=projectMatrix* modelMatrix *vec3(position,1);
gl_Position=vec4(vec2(_position),0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
primitive: "triangles",
uniforms: {
projectMatrix: projectMatrix,
modelMatrix: modelMatrix,
},
//frontFace: 'ccw'
colorMask: [true, true, true, true],
depth: {
enable: true,
// mask: false
},
// cull:{
// enable:true
// }
})
let fillRect = regl({
// blend:regl.prop('blend'),
attributes: {
position: regl.prop('position')
},
uniforms: {
color: regl.prop('color')
},
count: (c, props, bachId) => {
return props.position.length / 2
},
// ccw(逆时为正面,顺时为反面) 默认,cw(逆时为反面,顺时为正面)
frontFace: regl.prop('frontFace'),
cull: {
// 是否启用易除正面或反面,如果禁用,就同时绘制正反面
enable: regl.prop('cull.enable'),
// 剔除反面
face: regl.prop('cull.face'),
}
})
const guiData = addGuiScheme(this.$gui, {
source: {
cullFace: 'back',
cullFace2: 'back',
frontFace: "ccw"
},
schemes: {
cullFace: {
type: "list",
params: ['back', 'front']
},
cullFace2: {
type: "list",
params: ['back', 'front']
},
frontFace: {
type: "list",
params: ['ccw', 'cw']
}
}
})
let ticker = regl.frame(() => {
regl.clear({
color: [1, 1, 1, 1],
depth: 1
})
fillRectScope(() => {
fillRect({
position: clockwise,
color: [1, 0, 0, 1],
frontFace: guiData.frontFace,
cull: {
enable: true,
face: guiData.cullFace
}
})
fillRect({
position: inverse,
color: [0, 1, 0, 1],
frontFace: guiData.frontFace,
cull: {
enable: true,
face: guiData.cullFace2
}
})
})
})
}
}
}
})
addExample("blend", function () {
let width = 300, height = 300
/**
* blendFunc(sfactor, dfactor)
* 下列常数可用于 sfactor 和dfactor.
混合颜色的公式可以这样描述: color(RGBA) = (sourceColor * sfactor) + (destinationColor * dfactor). RBGA 值在 0 到 1 之间。
S=srcColor
C=blendColor
D=distColor
Constant Factor Description
持续的 因素 描述
gl.ZERO 0,0,0,0 将所有颜色乘以 0。
gl.ONE 1,1,1,1 将所有颜色乘以 1。
gl.SRC_COLOR R S , G S , B S , A S 将所有颜色乘以源颜色。
gl.ONE_MINUS_SRC_COLOR 1-R S , 1-G S , 1-B S , 1-A S 将所有颜色乘以 (1 减去每种源颜色)。
gl.DST_COLOR R D , G D , B D , A D 将所有颜色乘以目标颜色。
gl.ONE_MINUS_DST_COLOR 1-R D , 1-G D , 1-B D , 1-A D 将所有颜色乘以 (1 减去每种目标颜色)。
gl.SRC_ALPHA A S , A S , A S , A S 将所有颜色乘以源 Alpha 值。
gl.ONE_MINUS_SRC_ALPHA 1-A S , 1-A S , 1-A S , 1-A S 将所有颜色乘以 (1 减去源 Alpha 值)。
gl.DST_ALPHA A D , A D , A D , A D 将所有颜色乘以目标 alpha 值。
gl.ONE_MINUS_DST_ALPHA 1-A D , 1-A D , 1-A D , 1-A D 将所有颜色乘以 (1 减去目标 alpha 值)。
gl.CONSTANT_COLOR R C , G C , B C , A C 将所有颜色乘以一个常数颜色。
gl.ONE_MINUS_CONSTANT_COLOR 1-R C , 1-G C , 1-B C , 1-A C 将所有颜色乘以 (1 减去一个常数颜色)。
gl.CONSTANT_ALPHA A C , A C , A C , A C 将所有颜色乘以恒定的 alpha 值。
gl.ONE_MINUS_CONSTANT_ALPHA 1-A C , 1-A C , 1-A C , 1-A C 将所有颜色乘以 (1 减去恒定的 alpha 值)。
gl.SRC_ALPHA_SATURATE min(A S , 1 - A D ), min(A S , 1 - A D ), min(A S , 1 - A D ), 1 将 RGB 颜色乘以源 Alpha 值或 1 减去目标 Alpha 值中的较小者。alpha 值乘以 1。
将 RGB 颜色乘以源 alpha 值或 1 减去目标 alpha 值中的较小值。alpha 值乘以 1.
s0表示源(深度近),d表示目的(深度远),c表示有glBlendColor设置进来的常量。
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //默认是这个 sourceColor=[0,1,0,0.6] distColor=[1,0,0,1] rgba=sourceColor*0.6+(distColor*(1-0.6))
深度小的是source,深度大的是destination.
blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
以下常量可用于srcRGB、dstRGB、 srcAlpha和dstAlpha
混合因子的公式可以这样描述(所有 RGBA 值都在 0 和 1 之间):
颜色(RGB) = (sourceColor * srcRGB ) + (destinationColor * dstRGB )
颜色(A) = (sourceAlpha * srcAlpha ) + (destinationAlpha * dstAlpha )
*/
return {
template: `<div @mousedown="onDown" @mousemove="onMove" @mouseup="onUp">
<div>
<div class="row">
<div class="col-auto">
<div>canvas</div>
<canvas ref="preview" :width="width" :height="height"></canvas>
</div>
<div class="col-auto">
<div>webgl</div>
<div ref="container" :style="style"></div>
</div>
</div>
</div>
</div>`,
extends: BaseComponent,
data() {
return {
width: width,
height: height,
src: [],
extensions: ['EXT_blend_minmax', 'EXT_blend_minmax']
}
},
methods: {
calcBlendColor(type, sourcColor, distColor, blendColor) {
blendColor = blendColor || [0, 0, 0, 0]
let [r, g, b, a] = sourcColor
let [dr, dg, db, da] = distColor
let [br, bg, bb, ba] = blendColor
// let tColor=tinycolor.fromRatio({ r: color[0], g: color[1], b: color[] });
if (type === 'zero') {
return [0, 0, 0, 0]
} else if (type === 'one') {
return [1, 1, 1, 1]
} else if (type === 'src color') {
return [r, g, b, a]
} else if (type === 'one minus src color') {
return [1 - r, 1 - g, 1 - b, 1 - a]
} else if (type === 'dst color') {
return [dr, dg, db, da]
} else if (type === 'one minus dst color') {
return [1 - dr, 1 - dg, 1 - db, 1 - da]
} else if (type === 'src alpha') {
return [a, a, a, a]
} else if (type === 'one minus src alpha') {
return [1 - a, 1 - a, 1 - a, 1 - a]
} else if (type === 'dst alpha') {
return [da, da, da, da]
} else if (type === 'one minus dst alpha') {
return [1 - da, 1 - da, 1 - da, 1 - da]
} else if (type === 'constant color') {
return [br, bg, bb, ba]
} else if (type === 'one minus constant color') {
return [1 - br, 1 - bg, 1 - bb, 1 - ba]
} else if (type === 'constant alpha') {
return [ba, ba, ba, ba]
} else if (type === 'one minus constant alpha') {
return [1 - ba, 1 - ba, 1 - ba, 1 - ba]
} else if (type === 'src alpha saturate') {
// min(A S , 1 - A D ), min(A S , 1 - A D ), min(A S , 1 - A D ), 1
return [Math.min(a, 1 - da), Math.min(a, 1 - da), Math.min(a, 1 - da), 1]
}
return [1, 1, 1, 1]
},
multColor(a, b) {
return [a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]
},
createPreview() {
let preview = this.$refs.preview
let ctx = preview.getContext('2d')
let blendColor = [0, 0, 0, 0];
// let srcColor=[0, 1, 0, 1],distColor= [1, 0, 0, 1]
let width = this.width, height = this.height
let batchId = 0
let eachDrawRect = (rect, callback) => {
let x = rect[0], x2 = rect[0] + rect[2], y = rect[1], y2 = rect[1] + rect[3]
for (let r = y; r < y2; r++) {
for (let c = x; c < x2; c++) {
let index = (r * rect[2] + c) * 4
callback(index, r, c)
}
}
}
let drawBlendRect = (sourceColor, equation, sourceFactorType, sAlphaType, destinationFactorType, dAlphaType, rect) => {
batchId++
// 获取当前区域像表
let imageData = ctx.getImageData(rect[0], rect[1], rect[2], rect[3])
let data = imageData.data
eachDrawRect([0, 0, rect[2], rect[3]], (index, r, c) => {
let dstR = data[index] / 255, dstG = data[index + 1] / 255, dstB = data[index + 2] / 255, dstA = data[index + 3] / 255
let destinationColor = [dstR, dstG, dstB, dstA]
// 混合颜色的公式可以这样描述: color(RGBA) = (sourceColor * sfactor) + (destinationColor * dfactor). RBGA 值在 0 到 1 之间。
let sourceFactorColor = this.calcBlendColor(sourceFactorType, sourceColor, destinationColor)
let destinationFactorColor = this.calcBlendColor(destinationFactorType, sourceColor, destinationColor)
// 计算透明值
let sourceFactorAlpha = this.calcBlendColor(sAlphaType, sourceColor, destinationColor)
let destinationFactorAlpha = this.calcBlendColor(dAlphaType, sourceColor, destinationColor)
let sAlpha = sourceFactorAlpha[3], dAlpha = destinationFactorAlpha[3]
let rgba = []
if (equation == 'add') {
rgba[0] = sourceColor[0] * sourceFactorColor[0] + destinationColor[0] * destinationFactorColor[0]
rgba[1] = sourceColor[1] * sourceFactorColor[1] + destinationColor[1] * destinationFactorColor[1]
rgba[2] = sourceColor[2] * sourceFactorColor[2] + destinationColor[2] * destinationFactorColor[2]
// rgba[3]=sourceColor[3]*sourceFactorColor[3]+destinationColor[3]*destinationFactorColor[3]
rgba[3] = sourceColor[3] * sAlpha + destinationColor[3] * dAlpha
} else if (equation == 'subtract') {
rgba[0] = sourceColor[0] * sourceFactorColor[0] - destinationColor[0] * destinationFactorColor[0]
rgba[1] = sourceColor[1] * sourceFactorColor[1] - destinationColor[1] * destinationFactorColor[1]
rgba[2] = sourceColor[2] * sourceFactorColor[2] - destinationColor[2] * destinationFactorColor[2]
// rgba[3]=sourceColor[3]*sourceFactorColor[3]-destinationColor[3]*destinationFactorColor[3]
rgba[3] = sourceColor[3] * sAlpha - destinationColor[3] * dAlpha
} else if (equation == 'reverse subtract') {
rgba[0] = destinationColor[0] * destinationFactorColor[0] - sourceColor[0] * sourceFactorColor[0]
rgba[1] = destinationColor[1] * destinationFactorColor[1] - sourceColor[1] * sourceFactorColor[1]
rgba[2] = destinationColor[2] * destinationFactorColor[2] - sourceColor[2] * sourceFactorColor[2]
// rgba[3]=destinationColor[3]*destinationFactorColor[3]-sourceColor[3]*sourceFactorColor[3]
rgba[3] = destinationColor[3] * dAlpha - sourceColor[3] * sAlpha
} else if (equation == 'min') {
rgba[0] = Math.min(sourceColor[0] * sourceFactorColor[0], destinationColor[0] * destinationFactorColor[0])
rgba[1] = Math.min(sourceColor[1] * sourceFactorColor[1], destinationColor[1] * destinationFactorColor[1])
rgba[2] = Math.min(sourceColor[2] * sourceFactorColor[2], destinationColor[2] * destinationFactorColor[2])
// rgba[3]=Math.min(sourceColor[3]*sourceFactorColor[3],destinationColor[3]*destinationFactorColor[3])
rgba[3] = Math.min(sourceColor[3] * sAlpha, destinationColor[3] * dAlpha)
} else if (equation == 'max') {
rgba[0] = Math.max(sourceColor[0] * sourceFactorColor[0], destinationColor[0] * destinationFactorColor[0])
rgba[1] = Math.max(sourceColor[1] * sourceFactorColor[1], destinationColor[1] * destinationFactorColor[1])
rgba[2] = Math.max(sourceColor[2] * sourceFactorColor[2], destinationColor[2] * destinationFactorColor[2])
// rgba[3]=Math.max(sourceColor[3]*sourceFactorColor[3],destinationColor[3]*destinationFactorColor[3])
rgba[3] = Math.max(sourceColor[3] * sAlpha, destinationColor[3] * dAlpha)
}
data[index] = rgba[0] * 255
data[index + 1] = rgba[1] * 255
data[index + 2] = rgba[2] * 255
data[index + 3] = rgba[3] * 255
})
ctx.putImageData(imageData, rect[0], rect[1])
}
let drawRect = (sourceColor, rect) => {
let imageData = ctx.getImageData(rect[0], rect[1], rect[2], rect[3])
let data = imageData.data
eachDrawRect([0, 0, rect[2], rect[3]], (index, r, c) => {
data[index] = sourceColor[0] * 255
data[index + 1] = sourceColor[1] * 255
data[index + 2] = sourceColor[2] * 255
data[index + 3] = sourceColor[3] * 255
})
ctx.putImageData(imageData, rect[0], rect[1])
}
return {
clear: () => {
batchId = 0
//ctx.clearRect(0,0,width,height)
ctx.fillStyle = '#000'
ctx.fillRect(0, 0, width, height)
},
drawRect: drawRect,
drawBlendRect: drawBlendRect
}
},
createRect: function (x, y, w, h) {
return new Float32Array([
x, y,
x, y + h,
x + w, y,
x + w, y + h,
x + w, y,
x, y + h
])
},
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
let projectMatrix = mat3.create()
mat3.projection(projectMatrix, width, height)
const modelMatrix = mat3.create()
let destRect = this.createRect(100, 100, 100, 100)
let srcRect = this.createRect(150, 100, 100, 100)
let fillRect = regl({
vert: `
attribute vec2 position;
uniform mat3 projectMatrix;
uniform mat3 modelMatrix;
void main(){
vec3 _position=projectMatrix* modelMatrix *vec3(position,1);
gl_Position=vec4(vec2(_position),0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
primitive: "triangles",
attributes: {
position: regl.prop('position')
},
uniforms: {
projectMatrix: projectMatrix,
modelMatrix: modelMatrix,
color: regl.prop('color')
},
//frontFace: 'ccw'
// colorMask: [true, true, true, true],
depth: {
enable: true,
mask: true, // 是否禁用写入深度缓存区
range: [0, 1],
/*
gl.NEVER(从不通过)
gl.LESS(如果传入值小于深度缓冲区值则通过)
gl.EQUAL(如果传入值等于深度缓冲区值,则通过)
gl.LEQUAL(如果传入值小于或等于深度缓冲区值,则通过)
gl.GREATER(如果传入值大于深度缓冲区值则通过)
gl.NOTEQUAL(如果传入值不等于深度缓冲区值则通过)
gl.GEQUAL(如果传入值大于或等于深度缓冲区值则通过)
gl.ALWAYS(总是通过)
*/
func: regl.prop('depthFunc')
},
stencil:{
enable:regl.prop('stencil.enable'),
mask:regl.prop('stencil.mask'),
func:regl.prop('stencil.func'),
op:regl.prop('stencil.op'),
// opBack:regl.prop('stencil.opBack'),
},
blend: {
enable: regl.prop('blend.enable'),
equation: regl.prop('blend.equation'),
color: regl.prop('blendColor'),
func: function (ctx, prop, batchId) {
return prop.blend.enable ? prop.blend.func : {
srcRGB: "one",
srcAlpha: 1,
dstRGB: "zero",
dstAlpha: 0
}
}
},
count: function (ctx, props) {
return props.position.length / 2
}
})
const stencilOperationType =
/* `gl.ZERO` */
["zero" ,
/* `gl.KEEP` */
"keep" ,
/* `gl.REPLACE` */
"replace" ,
/* `gl.INVERT` */
"invert" ,
/* `gl.INCR` */
"increment" ,
/* `gl.DECR` */
"decrement" ,
/* `gl.INCR_WRAP` */
"increment wrap" ,
/* `gl.DECR_WRAP` */
"decrement wrap"];
/**
gl.KEEP
保持当前值。
gl.ZERO
将模板缓冲区值设置为 0。
gl.REPLACE
将模板缓冲区值设置为 指定的参考值 WebGLRenderingContext.stencilFunc()。
gl.INCR
增加当前模板缓冲区值。钳位到最大可表示的无符号值。
gl.INCR_WRAP
增加当前模板缓冲区值。当递增最大可表示无符号值时,将模板缓冲区值包装为零。
gl.DECR
减少当前模板缓冲区值。钳位到 0。
gl.DECR_WRAP
减少当前模板缓冲区值。当递减模板缓冲区值 0 时,将模板缓冲区值包装为最大可表示无符号值。
gl.INVERT
按位反转当前模板缓冲区值。
**/
let blendFuncs = {
'zero': 'gl.ZERO',
'one': 'gl.ONE',
'src color': 'gl.SRC_COLOR',
'one minus src color': 'gl.ONE_MINUS_SRC_COLOR',
'src alpha': 'gl.SRC_ALPHA',
'one minus src alpha': 'gl.ONE_MINUS_SRC_ALPHA',
'dst color': 'gl.DST_COLOR',
'one minus dst color': 'gl.ONE_MINUS_DST_COLOR',
'dst alpha': 'gl.DST_ALPHA',
'one minus dst alpha': 'gl.ONE_MINUS_DST_ALPHA',
'constant color': 'gl.CONSTANT_COLOR',
'one minus constant color': 'gl.ONE_MINUS_CONSTANT_COLOR',
'constant alpha': 'gl.CONSTANT_ALPHA',
'one minus constant alpha': 'gl.ONE_MINUS_CONSTANT_ALPHA',
'src alpha saturate': 'gl.SRC_ALPHA_SATURATE',
}
blendFuncs = Object.keys(blendFuncs).reduce((a, b) => {
a[blendFuncs[b]] = b
return a
}, {})
const equations = ['add', 'subtract', 'reverse subtract', 'min', 'max']
const guiData = addGuiScheme(this.$gui, {
source: {
depthFunc: 'lequal',// webgl默认是less
blendColor:'0,0,0,0',
blendEnable: false,
equation: 'add',
color:'1,0,0,1',
srcFactor: 'one',
dstFactor: 'zero',
srcAlpha: 'one',
dstAlpha: 'zero',
blendEnable2: true,
equation2: 'add',
color2:'0,1,0,0.5',
srcFactor2: 'one',
dstFactor2: 'zero',
srcAlpha2: 'one',
dstAlpha2: 'zero',
stencil:{
enable:true,
mask:1,
func:{
cmp:"always",
ref:0,
mask:1,
},
op:{
fail:"keep",
zfail:"keep",
zpass:"keep"
}
},
stencil2:{
enable:false,
mask:1,
func:{
cmp:"always",
ref:0,
mask:1,
},
op:{
fail:"keep",
zfail:"keep",
zpass:"keep"
}
}
},
schemes: {
depthFunc: {
type: "list",
params: ["never",
/* `gl.ALWAYS` */
"always",
/* `gl.LESS` */
"less", "<",
/* `gl.LEQUAL` */
"lequal", "<=",
/* `gl.GREATER` */
"greater", ">",
/* `gl.GEQUAL` */
"gequal", ">=",
/* `gl.EQUAL` */
"equal", "=",
/* `gl.NOTEQUAL` */
"notequal", "!="]
},
srcFactor: {
type: "list",
params: blendFuncs
},
dstFactor: {
type: "list",
params: blendFuncs
},
srcFactor2: {
type: "list",
params: blendFuncs
},
dstFactor2: {
type: "list",
params: blendFuncs
},
equation: {
type: "list",
params: equations
},
equation2: {
type: "list",
params: equations
},
srcAlpha: {
type: "list",
params: blendFuncs
},
dstAlpha: {
type: "list",
params: blendFuncs
},
srcAlpha2: {
type: "list",
params: blendFuncs
},
dstAlpha2: {
type: "list",
params: blendFuncs
},
stencil:{
schemes:{
func:{
schemes:{
cmp:{
type:"list",
params: ["never",
/* `gl.ALWAYS` */
"always",
/* `gl.LESS` */
"less", "<",
/* `gl.LEQUAL` */
"lequal", "<=",
/* `gl.GREATER` */
"greater", ">",
/* `gl.GEQUAL` */
"gequal", ">=",
/* `gl.EQUAL` */
"equal", "=",
/* `gl.NOTEQUAL` */
"notequal", "!="]
}
}
},
op:{
schemes:{
fail:{
type:"list",
params:stencilOperationType
},
zfail:{
type:"list",
params:stencilOperationType
},
zpass:{
type:"list",
params:stencilOperationType
}
}
}
}
},
stencil2:{
schemes:{
func:{
schemes:{
cmp:{
type:"list",
params: ["never",
/* `gl.ALWAYS` */
"always",
/* `gl.LESS` */
"less", "<",
/* `gl.LEQUAL` */
"lequal", "<=",
/* `gl.GREATER` */
"greater", ">",
/* `gl.GEQUAL` */
"gequal", ">=",
/* `gl.EQUAL` */
"equal", "=",
/* `gl.NOTEQUAL` */
"notequal", "!="]
}
}
},
op:{
schemes:{
fail:{
type:"list",
params:stencilOperationType
},
zfail:{
type:"list",
params:stencilOperationType
},
zpass:{
type:"list",
params:stencilOperationType
}
}
}
}
}
},
onFinishChange: () => {
renderDraw()
}
})
const { clear, drawRect, drawBlendRect } = this.createPreview()
const renderDraw = () => {
let color=guiData.color.split(',').map(Number)
let color2=guiData.color2.split(',').map(Number)
let blendColor=guiData.blendColor.split(',').map(Number)
regl.clear({
color: [0, 0, 0, 1],
depth: 1,
stencil:0
})
fillRect([{
position: destRect,
color: color,
blendColor:blendColor,
blend: {
enable: guiData.blendEnable,
func: {
srcRGB: guiData.srcFactor,
srcAlpha: guiData.srcAlpha,
dstRGB: guiData.dstFactor,
dstAlpha: guiData.dstAlpha
},
equation: guiData.equation
},
stencil:{
enable:guiData.stencil.enable,
mask:guiData.stencil.mask,
func:guiData.stencil.func,
op:guiData.stencil.op
},
depthFunc: guiData.depthFunc
}, {
position: srcRect,
color: color2,
blendColor:blendColor,
blend: {
enable: guiData.blendEnable2,
func: {
srcRGB: guiData.srcFactor2,
srcAlpha: guiData.srcAlpha2,
dstRGB: guiData.dstFactor2,
dstAlpha: guiData.dstAlpha2
},
equation: guiData.equation2
},
stencil:{
enable:guiData.stencil2.enable,
mask:guiData.stencil2.mask,
func:guiData.stencil2.func,
op:guiData.stencil2.op
},
depthFunc: guiData.depthFunc
}])
clear()
drawBlendRect([1, 0, 0, 1], guiData.equation, guiData.srcFactor, guiData.srcAlpha, guiData.dstFactor, guiData.dstAlpha, [100, 100, 100, 100])
drawBlendRect([0, 1, 0, 1], guiData.equation2, guiData.srcFactor2, guiData.srcAlpha2, guiData.dstFactor2, guiData.dstAlpha2, [150, 100, 100, 100])
}
renderDraw()
}
}
}
})
addExample("渲染大量数量(快)", function () {
let box = new THREE.BoxBufferGeometry(1, 1, 1)
let boxObj = new THREE.Object3D()
let boxPosition = box.attributes.position.array
let boxElements = box.getIndex().array
// let camera = new THREE.OrthographicCamera(800 / -2, 800 / 2, 600 / 2, 600 / -2, 0.1, 1000)
let camera = new THREE.PerspectiveCamera(50, 800 / 600, 0.1, 10000)
camera.position.set(0, 1, 5)
camera.updateWorldMatrix(false, false)
return {
template: `<div><canvas width="800" height="600" ref="main"></canvas></div>`,
data() { return {}; },
mounted() {
var container = this.$refs.main;
let regl = window.regl = createREGL({
canvas: container,
})
var oribt = new THREE.OrbitControls(camera, regl._gl.canvas)
let setupDraw = regl({
context: {
projectionMatrix(context, props) {
return camera.projectionMatrix.elements
}
},
attributes: {
position: regl.prop('position'),
},
uniforms: {
projectionMatrix: regl.context('projectionMatrix'),
// modelViewMatrix:regl.prop('modelViewMatrix'),
},
elements: regl.prop('elements')
})
let drawRect = regl({
vert: `
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main(){
gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
// attributes: {
// position: regl.prop('position'),
// },
uniforms: {
modelViewMatrix: regl.prop('modelViewMatrix'),
color: regl.prop('color')
},
//primitive: 'triangle',
// elements:regl.prop('elements')
//count: boxPosition.length / 3
})
// console.log('boxElements',boxElements)
// 生成
const buildCube = (count) => {
const drawObjects = new THREE.Object3D()
for (let i = 0; i < count; i++) {
let obj = new THREE.Object3D();
obj.position.z = -i
obj.drawObj = {
color: [1, Math.random(), Math.random(), 1],
modelViewMatrix: obj.modelViewMatrix.elements
}
drawObjects.add(obj)
}
return drawObjects
}
const drawObjects = buildCube(5000)
const stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)
const boxElement = regl.elements({
primitive: "triangle",
data: boxElements,
type: "uint16",
})
const render = () => {
stats.begin()
regl.clear({
color: [1, 1, 1, 1],
depth: 1
})
camera.updateMatrixWorld()
drawObjects.updateMatrixWorld();
setupDraw({
position: boxPosition,
elements: boxElement,
}, (context, props) => {
const draws = []
drawObjects.children.forEach((obj) => {
obj.rotation.y+=0.01;
obj.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, obj.matrixWorld)
obj.drawObj.modelViewMatrix = obj.modelViewMatrix.elements
draws.push(obj.drawObj)
//obj.position.x+=1
})
drawRect(draws)
})
stats.end()
}
regl.frame(render)
oribt.addEventListener('change', () => {
console.log('oribt')
// camera.updateProjectionMatrix()
// render()
})
// render()
}
}
})
addExample("渲染大量数量(慢)", function () {
let box = new THREE.BoxBufferGeometry(1, 1, 1)
let boxObj = new THREE.Object3D()
let boxPosition = box.attributes.position.array
let boxElements = box.getIndex().array
// let camera = new THREE.OrthographicCamera(800 / -2, 800 / 2, 600 / 2, 600 / -2, 0.1, 1000)
let camera = new THREE.PerspectiveCamera(50, 800 / 600, 0.1, 10000)
camera.position.set(0, 1, 5)
camera.updateWorldMatrix(false, false)
return {
template: `<div><canvas width="800" height="600" ref="main"></canvas></div>`,
data() { return {}; },
mounted() {
var container = this.$refs.main;
let regl = window.regl = createREGL({
canvas: container,
})
var oribt = new THREE.OrbitControls(camera, regl._gl.canvas)
let setupDraw = regl({
context: {
projectionMatrix(context, props) {
return camera.projectionMatrix.elements
}
},
uniforms: {
projectionMatrix: regl.context('projectionMatrix'),
// modelViewMatrix:regl.prop('modelViewMatrix'),
}
})
let drawRect = regl({
vert: `
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main(){
gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
attributes: {
position: regl.prop('position'),
},
uniforms: {
modelViewMatrix: regl.prop('modelViewMatrix'),
color: regl.prop('color')
},
elements: regl.prop('elements')
//primitive: 'triangle',
// elements:regl.prop('elements')
//count: boxPosition.length / 3
})
// console.log('boxElements',boxElements)
// 生成
const buildCube = (count) => {
const drawObjects = new THREE.Object3D()
for (let i = 0; i < count; i++) {
let obj = new THREE.Object3D();
obj.position.z = -i
obj.drawObj = {
position: boxPosition,
elements: boxElement,
color: [1, Math.random(), Math.random(), 1],
modelViewMatrix: obj.modelViewMatrix.elements
}
drawObjects.add(obj)
}
return drawObjects
}
const stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)
const boxElement = regl.elements({
primitive: "triangle",
data: boxElements,
type: "uint16",
})
const drawObjects = buildCube(5000)
const render = () => {
stats.begin()
regl.clear({
color: [1, 1, 1, 1],
depth: 1
})
camera.updateMatrixWorld()
drawObjects.updateMatrixWorld();
setupDraw({
}, (context, props) => {
const draws = []
drawObjects.children.forEach((obj) => {
obj.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse, obj.matrixWorld)
obj.drawObj.modelViewMatrix = obj.modelViewMatrix.elements
draws.push(obj.drawObj)
//obj.position.x+=1
})
drawRect(draws)
})
stats.end()
}
regl.frame(render)
oribt.addEventListener('change', () => {
console.log('oribt')
// camera.updateProjectionMatrix()
// render()
})
// render()
}
}
})
addExample("所有blend效果", function () {
return {
extends: BaseComponent,
data() {
return {
extensions:['EXT_blend_minmax','EXT_float_blend'],
width: 1100,
height: 600,
devicePixelRatio:1
};
},
computed: {},
methods: {
/**
* @param {createREGL.Regl} regl
*/
init(regl) {
function createRect(x, y, w, h) {
return new Float32Array([
x, y,
x, y + h,
x + w, y,
x + w, y + h,
x + w, y,
x, y + h
])
}
const cameraPosition = [0, 0]
const superDraw = regl({
uniforms: {
resolution: (ctx) => {
// console.log('viewportWidth',ctx.viewportHeight)
return [ctx.viewportWidth, ctx.viewportHeight];
},
offset: () => {
return cameraPosition
}
},
depth: {
enable: regl.prop('depth.enable'),
func: regl.prop('depth.func')
},
})
let event = null
this.$on('down', (e) => {
event = { x: e.clientX - cameraPosition[0], y: e.clientY - cameraPosition[1] }
})
this.$on('move', (e) => {
if (event) {
cameraPosition[0] = e.clientX - event.x;
cameraPosition[1] = e.clientY - event.y;
renderGraphics()
}
})
this.$on('up', (e) => {
event = null
})
const draw = regl({
vert: `
attribute vec2 position;
uniform vec2 resolution;
uniform vec2 offset;
uniform vec2 selfOffset;
void main(){
// 从像素坐标转换到 0.0 到 1.0
vec2 zeroToOne = (position+offset+selfOffset) / resolution;
// 再把 0->1 转换 0->2
vec2 zeroToTwo = zeroToOne * vec2(2.0,-2.0);
// 把 0->2 转换到 -1->+1 (裁剪空间)
vec2 clipSpace = zeroToTwo - vec2(1.0,-1.0);
gl_Position = vec4(clipSpace, 0, 1);
// gl_Position=vec4(bPos,0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
attributes: {
position: regl.prop('position')
},
uniforms: {
color: regl.prop('color'),
selfOffset: (ctx, prop) => {
return prop.selfOffset ? prop.selfOffset : [0, 0]
}
},
blend: {
enable: regl.prop('blend.enable'),
equation: regl.prop('blend.equation'),
func: function (ctx, prop, batchId) {
//{ src: 'src alpha', dst: 'one minus src alpha' }
// {
// srcRGB:prop.blend.func.src,
// dstRGB:prop.blend.func.dst,
// srcAlpha:"one",
// dstAlpha:"one"
// }
return prop.blend.enable ?{
srcRGB:prop.blend.func.src,
dstRGB:prop.blend.func.dst,
srcAlpha:prop.blend.func.srcAlpha,
dstAlpha:prop.blend.func.dstAlpha
}: false ? { src: 'src alpha', dst: 'one minus src alpha' } : {
src: "one",
dst: "zero"
}
},
color: [0, 0, 0, 0]
},
primitive: "triangles",
elements: regl.prop('elements'),
count: (ctx, prop) => {
return prop.elements ? prop.elements.length : prop.position.length / 2
}
})
// let ext= regl._gl.getExtension('EXT_blend_minmax')
// console.log('e',ext)
const equations = ['add', 'subtract', 'reverse subtract', 'min', 'max']
const depthFuncs = [ /* `gl.NEVER` */
"never",
/* `gl.ALWAYS` */
"always",
/* `gl.LESS` */
"less",
/* `gl.LEQUAL` */
"lequal",
/* `gl.GREATER` */
"greater",
/* `gl.GEQUAL` */
"gequal",
/* `gl.EQUAL` */
"equal",
/* `gl.NOTEQUAL` */
"notequal"];
const blends = [/* `gl.ZERO` */"zero",
/* `gl.ONE` */
"one",
/* `gl.SRC_COLOR` */
"src color",
/* `gl.ONE_MINUS_SRC_COLOR` */
"one minus src color",
/* `gl.SRC_ALPHA` */
"src alpha",
/* `gl.ONE_MINUS_SRC_ALPHA` */
"one minus src alpha",
/* `gl.DST_COLOR` */
"dst color",
/* `gl.ONE_MINUS_DST_COLOR` */
"one minus dst color",
/* `gl.DST_ALPHA` */
"dst alpha",
/* `gl.ONE_MINUS_DST_ALPHA` */
"one minus dst alpha",
/* `gl.CONSTANT_COLOR` */
"constant color",
/* `gl.ONE_MINUS_CONSTANT_COLOR` */
"one minus constant color",
/* `gl.CONSTANT_ALPHA` */
"constant alpha" ,
/* `gl.ONE_MINUS_CONSTANT_ALPHA` */
"one minus constant alpha" ,
/* `gl.SRC_ALPHA_SATURATE` */
// "src alpha saturate"
];
const guiData = addGuiScheme(this.$gui, {
source: {
depthFunc: "lequal",
equation: 'add',
src: "one",
dst: 'zero',
},
schemes: {
depthFunc: {
type: "list",
params: depthFuncs
},
equation: {
type: "list",
params: equations
},
src: {
type: "list",
params: blends
},
dst: {
type: "list",
params: blends
},
},
onChange() {
renderGraphics()
}
})
function crossRecursive(...values) {
let maxDepth = values.length, count = 0;
let result = []
function processData(root, depth, parents = []) {
let isLast = depth + 1 >= maxDepth;
root[depth].forEach(function (item, index) {
if (isLast) {
result.push([].concat(parents, item))
} else {
parents.push(item)
processData(root, depth + 1, parents)
parents.pop()
}
})
}
if (values.length > 0) {
processData(values, 0)
}
return result
}
const schemes = crossRecursive(blends,blends,blends,blends)
const textMap = new Map()
const that = this;
function updateDraw() {
regl.clear({
color: [1, 1, 1, 1],
depth: 1
})
let drawObjects = []
const drawTexts = []
let equation=guiData.equation
// if(equation==='min'){
// equation=ext.MIN_EXT
// }
// if(equation==='max'){
// equation=ext.MAX_EXT
// }
console.log('schemes', schemes.length)
schemes.forEach((scheme, i) => {
let columnCount=8
let row = Math.floor(i / columnCount), sh2 = 75, sw2 = 75, sw = 50, sh = 50;
let col = i % columnCount;
let x = col * sw2 + (col + 1) * 50;
let y = row * sh2 + (row + 1) * 50;
let y2=y+cameraPosition[1];
if(y2<-50||y2>600){
return false
}
drawObjects.push({
y:y,
x:y,
position: createRect(x, y, sw, sh),
color: [0.2, 0.7, 0.2, 1],
blend: {
enable: true,
equation: equation,
func: {
src: guiData.src,
dst: guiData.dst,
srcAlpha:"one",
dstAlpha:"zero",
}
}
})
drawObjects.push({
y:y+sh/2,
x:x,
position: createRect(x + sw / 2, y + sh / 2, sw, sh),
color: [0.3, 0.3, 0.3, 1],
blend: {
enable: true,
equation: equation,
func: {
src: scheme[0],
dst: scheme[1],
// srcAlpha:"one",
// dstAlpha:"zero",
srcAlpha: scheme[2],
dstAlpha: scheme[3],
}
}
})
// let cacheKey=scheme.join(' ')
// if(!textMap.has(cacheKey)){
// const textMesh = VectorizeText.default('你好', {
// // width:300,
// // height:100,
// textAlign: 'center',
// textBaseline: 'middle'
// })
// textMap.set(cacheKey,textMesh)
// }
drawTexts.push({
x: x,
y: y + sh * 1.5,
text: scheme.join('<br/>')
// position:textMap.get(cacheKey).positions,
// color:[0,0,0,1],
// selfOffset:[x,y],
// elements:textMap.get(cacheKey).edges,
// blend:{
// enable:false,
// equation:'add',
// func:{
// }
// }
})
})
// drawObjects=drawObjects.filter((d,i)=>{
// let y2=d.y+cameraPosition[1];
// if(y2<-50||y2>600){
// return false
// }
// return true
// })
superDraw({
depth: {
enable: true,
func: guiData.depthFunc
}
}, (ctx, props) => {
draw(drawObjects)
})
let frament = document.createDocumentFragment()
let bound = that.$refs.container.getBoundingClientRect()
drawTexts.forEach((d,i) => {
// let y2=d.y+cameraPosition[1];
// if(y2<-50||y2>600){
// return;
// }
let span = document.createElement('span')
span.title = d.text;
span.innerHTML = d.text
span.style.textOverflow = "ellipsis"
span.style.whiteSpace = "nowrap";
span.style.overflow = 'hidden'
span.style.width = '125px'
span.style.fontSize = '12px'
span.style.position = 'absolute'
span.style.pointerEvents = 'auto'
span.style.transform = `scale(${1/that.devicePixelRatio}) translate(${d.x + cameraPosition[0]}px,${d.y + cameraPosition[1]}px)`
// textContainer.style.transform=`scale(${1/window.devicePixelRatio})`
span.style.transformOrigin="left top"
frament.appendChild(span)
let minY = 0, maxY = bound.bottom;
})
// console.log('row',drawObjects.length,drawTexts.length,cameraPosition[1])
let textContainer = document.getElementById('textContainer')
if (!textContainer) {
let bound = that.$refs.container.getBoundingClientRect()
textContainer = document.createElement('div');
textContainer.id = 'textContainer'
textContainer.style.width = bound.width + 'px'
textContainer.style.height = bound.height + 'px'
textContainer.style.position = 'absolute'
textContainer.style.userSelect = "none"
textContainer.style.pointerEvents = 'none'
textContainer.style.left = bound.left + 'px'
textContainer.style.top = bound.top + 'px'
textContainer.style.overflow = "hidden"
//textContainer.style.zIndex=-1
document.body.appendChild(textContainer)
} else {
textContainer.innerHTML = ''
}
textContainer.appendChild(frament)
//textContainer.style.paddingTop=cameraPosition[1]+'px';
isRendering = false
}
let isRendering = false
function renderGraphics() {
if (isRendering) {
return
}
isRendering = true;
window.requestAnimationFrame(updateDraw)
}
renderGraphics()
}
},
mounted() {
}
}
})
addExample("2d纹理",function(){
return {
extends:BaseComponent,
data(){ return {devicePixelRatio:1}},
methods:{
/**
* @param {createREGL.Regl} regl
*/
init(regl){
const setup=regl({
context:{
uResolution(ctx){
return [ctx.viewportWidth,ctx.viewportHeight]
}
},
uniforms:{
uResolution:regl.context('uResolution')
}
})
const drawTexture=regl({
vert:`
attribute vec2 aPosition;
uniform vec2 uResolution;
uniform vec2 u_offset;
void main(){
// 从屏幕转换为-1>1.0
vec2 pos=(aPosition/uResolution)*2.0-1.0;
vec2 clipPosition=pos*vec2(1.0,-1.0)+u_offset;// y轴倒过来
// v_positionWithOffset = pos + u_offset;
gl_Position=vec4(clipPosition,0,1.0);
}
`,
frag:`
precision mediump float;
uniform sampler2D u_texture;
void main(){
vec2 texcoord = vec2(0.5, 0.5); // 获取纹理中心的值
gl_FragColor = texture2D(u_texture, texcoord);
}
`,
attributes:{
aPosition:regl.prop('aPosition')
},
uniforms:{
u_texture:regl.prop('u_texture'),
u_offset:[0,0]
},
primitive:"triangles",
count:regl.prop('count')
})
regl.clear({
color:[1,1,1,1]
})
let box=[
[100,100],
[200,100],
[100,200],
[200,200],
[100,200],
[200,100]
]
let texture=regl.texture({
width:3,
height:1,
type:"uint8",
format:"rgba",
data:new Uint8ClampedArray([
255,0,0,255,
0,255,0,255,
0,0,255,255,
])
})
setup(()=>{
drawTexture({
aPosition:box,
u_texture:texture,
count:box.length
})
})
}
}
}
})
addExample("着色片段",function(){
return {
extends:BaseComponent,
data(){ return {devicePixelRatio:1}},
methods:{
/**
* @param {createREGL.Regl} regl
*/
init(regl){
const setup=regl({
context:{
uResolution(ctx){
return [ctx.viewportWidth,ctx.viewportHeight]
}
},
uniforms:{
uResolution:regl.context('uResolution')
}
})
const drawTexture=regl({
vert:`
attribute vec2 aPosition;
uniform vec2 uResolution;
uniform vec2 u_offset;
varying vec4 v_positionWithOffset;
void main(){
// 从屏幕转换为-1>1.0
vec2 pos=(aPosition/uResolution)*2.0-1.0;
vec2 clipPosition=pos*vec2(1.0,-1.0)+u_offset;// y轴倒过来
gl_Position=vec4(clipPosition,0,1.0);
v_positionWithOffset =vec4(clipPosition,0,1.0);
}
`,
frag:`
precision mediump float;
varying vec4 v_positionWithOffset;
void main(){
// 从裁剪空间 (-1 <-> +1) 转换到颜色空间 (0 -> 1).
// 片元执行次数,如果采用的是triangles绘制,相当要把三角形内的顶点都要着色
vec4 color = v_positionWithOffset * 0.5 + 0.5;
gl_FragColor = color;
}
`,
attributes:{
aPosition:regl.prop('aPosition')
},
uniforms:{
u_offset:regl.prop('u_offset')
// u_texture:regl.prop('u_texture')
},
primitive:"triangles",
count:regl.prop('count')
})
regl.clear({
color:[1,1,1,1]
})
let createBox=(x,y,w,h)=>{
return [
[x,y],
[x+w,y],
[x,y+h],
[x+w,y+h],
[x,y+h],
[x+w,y]
]
}
let box=createBox(0,0,this.width,this.height)
let texture=regl.texture({
width:3,
height:1,
type:"uint8",
format:"rgba",
data:new Uint8ClampedArray([
255,0,0,255,
0,255,0,255,
0,0,255,255,
])
})
setup(()=>{
drawTexture({
aPosition:box,
u_offset:[0,0],
// u_texture:texture,
count:box.length
})
})
}
}
}
})
addExample("FBO", function () {
// let camera = new THREE.OrthographicCamera(800 / -2, 800 / 2, 600 / 2, 600 / -2, 0.1, 1000)
let camera = new THREE.PerspectiveCamera(50, 800 / 600, 0.1, 10000)
camera.position.set(0, 1, 5)
const scene=new THREE.Scene();
return {
template: `<div><canvas width="800" height="600" ref="main"></canvas></div>`,
data() { return {}; },
mounted() {
var container = this.$refs.main;
let regl = window.regl = createREGL({
canvas: container,
})
var oribt = new THREE.OrbitControls(camera, regl._gl.canvas)
let drawBox = regl({
vert: `
attribute vec3 position;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
void main(){
gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
attributes: {
position: regl.prop('position'),
},
uniforms: {
projectionMatrix:regl.prop('projectionMatrix'),
modelViewMatrix: regl.prop('modelViewMatrix'),
color: regl.prop('color')
},
//primitive: 'triangle',
elements:regl.prop('elements')
//count: boxPosition.length / 3
})
const stats = new Stats()
stats.showPanel(0)
document.body.appendChild(stats.dom)
const box=new THREE.Mesh(new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({
color:0xff0000
}))
scene.add(box)
const geometryMap=new Map()
/**
* @param {THREE.Mesh} mesh
* @param {THREE.PerspectiveCamera} camera
*/
const renderMesh=(mesh,camera)=>{
const {geometry,material}=mesh
const geometryPosition=geometry.getAttribute('position').array;
const geometryElements = geometry.getIndex().array
const modelViewMatrix=mesh.modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,mesh.matrixWorld);
if(geometry.type==="BoxGeometry"){
drawBox({
elements:geometryElements,
position:geometryPosition,
projectionMatrix:camera.projectionMatrix.elements,
modelViewMatrix:modelViewMatrix.elements,
color:[material.color.r,material.color.g,material.color.b,1]
})
}
}
/**
*
* @param {THREE.Scene} scene
* @param {THREE.PerspectiveCamera} camera
*/
const renderScene=(scene,camera)=>{
if(camera.matrixAutoUpdate){
camera.updateMatrixWorld()
}
scene.updateMatrixWorld()
scene.traverseVisible(obj=>{
if(obj.type==='Mesh'){
renderMesh(obj,camera)
}
})
}
const render = () => {
stats.begin()
regl.clear({
color: [1, 1, 1, 1],
depth: 1
})
renderScene(scene,camera)
stats.end()
}
regl.frame(render)
oribt.addEventListener('change', () => {
console.log('oribt')
// camera.updateProjectionMatrix()
// render()
})
// render()
}
}
})
addExample("stencil模板测试",function(){
return {
template:`<div><canvas ref="canvas"></canvas></div>`,
data(){ return {};},
computed:{},
methods:{},
mounted(){
var width=500,height=500;
var canvas=this.$refs.canvas;
canvas.width=width
canvas.height=height
let projectMatrix = mat3.create()
mat3.projection(projectMatrix, width, height)
function createRect(x,y,w,h){
return new Float32Array([
x, y,
x, y + h,
x + w, y,
x + w, y + h,
x + w, y,
x, y + h
])
}
var regl=createREGL({
canvas:canvas,
attributes:{
antialias:true,
stencil:true,// 开启模板测试
}
})
var drawRect=regl({
vert: `
attribute vec2 position;
uniform mat3 projectMatrix;
uniform mat3 modelMatrix;
void main(){
vec3 _position=projectMatrix* modelMatrix *vec3(position,1);
gl_Position=vec4(vec2(_position),0,1.0);
}
`,
frag: `
precision mediump float;
uniform vec4 color;
void main(){
gl_FragColor=color;
}
`,
primitive: "triangles",
attributes: {
position: regl.prop('position')
},
uniforms: {
projectMatrix: projectMatrix,
modelMatrix: regl.prop('modelMatrix'),
color: regl.prop('color')
},
//frontFace: 'ccw'
colorMask: regl.prop('colorMask'),
depth: {
enable: true,
mask: true, // 是否禁用写入深度缓存区
range: [0, 1],
/*
通过就写入深度缓冲区,深度缓冲区,主要用于图层的远近顺序
该函数将传入的像素深度与当前深度缓冲区值进行比较。
gl.NEVER(从不通过)
gl.LESS(如果传入值小于深度缓冲区值则通过)
gl.EQUAL(如果传入值等于深度缓冲区值,则通过)
gl.LEQUAL(如果传入值小于或等于深度缓冲区值,则通过)
gl.GREATER(如果传入值大于深度缓冲区值则通过)
gl.NOTEQUAL(如果传入值不等于深度缓冲区值则通过)
gl.GEQUAL(如果传入值大于或等于深度缓冲区值则通过)
gl.ALWAYS(总是通过)
*/
func:"lequal"
},
stencil:{
enable:regl.prop('stencil.enable'),// 开启模板测试
//gl.stencilMask(0xFF); // 每一位写入模板缓冲时都保持原样
//gl.stencilMask(0x00); // 每一位在写入模板缓冲时都会变成0(禁用写入)
mask:regl.prop('stencil.mask'),
//确定后续的渲染,和当前模板缓冲中的像素模板值对比之后,是否丢弃掉渲染的结果。
/*
gl.NEVER: 永不通过。
gl.LESS:通过如果 (ref & mask) < (stencil & mask)。
gl.EQUAL:通过如果 (ref & mask) = (stencil & mask)。
gl.LEQUAL:通过如果 (ref & mask) <= (stencil & mask)。
gl.GREATER:通过如果 (ref & mask) > (stencil & mask)。
gl.NOTEQUAL:通过如果 (ref & mask) !== (stencil & mask)。
gl.GEQUAL:通过如果 (ref & mask) >= (stencil & mask)。
gl.ALWAYS: 总是通过。
*/
func:regl.prop('stencil.func'),
// 设定如何根据下一次渲染的结果来更新模板缓冲中的值。
//sfail:模板测试失败时采取的行为。
// dpfail:模板测试通过,但深度测试失败时采取的行为。
// dppass:模板测试和深度测试都通过时采取的行为。
/**
* 具体可以传递的参数如下:
KEEP(不改变,这也是默认值)
ZERO(回零)
REPLACE(使用测试条件中的设定值来代替当前模板值,stencilFunc方法中的ref参数)
INCR(增加1,但如果已经是最大值,则保持不变)
INCR_WRAP(增加1,但如果已经是最大值,则从零重新开始)
DECR(减少1,但如果已经是零,则保持不变)
DECR_WRAP(减少1,但如果已经是零,则重新设置为最大值)
INVERT(按位取反)
*/
/* `gl.ZERO` */
// "zero" |
// /* `gl.KEEP` */
// "keep" |
// /* `gl.REPLACE` */
// "replace" |
// /* `gl.INVERT` */
// "invert" |
// /* `gl.INCR` */
// "increment" |
// /* `gl.DECR` */
// "decrement" |
// /* `gl.INCR_WRAP` */
// "increment wrap" |
// /* `gl.DECR_WRAP` */
// "decrement wrap";
op:regl.prop('stencil.op'),
// opBack:regl.prop('stencil.opBack'),
},
count: function (ctx, props) {
return props.position.length / 2
}
});
const rectPosition=createRect(0,0,100,100);
const modelMatrix1=mat3.create();
mat3.translate(modelMatrix1,modelMatrix1,[100,100])
const modelMatrix2=mat3.create();
mat3.translate(modelMatrix2,modelMatrix2,[150,100])
const update=()=>{
// regl._gl.colorMask(true,false,true,true) // 控制颜色缓冲区,哪个颜色可以写入
regl.clear({
color:[1,1,1,1],
depth:1,
stencil:0, // 清除模板缓冲区,将值初始为0
});
// 将当前这个矩形,
drawRect({
color:[1,0,0,1],
position:rectPosition,
modelMatrix:modelMatrix1,
// 设置是否可以写入颜色缓冲区
colorMask:[true,true,true,true],
stencil:{
enable:true,
mask:0x4,// 允许写入的mask
func:{
cmp: 'always',
ref: 7,
mask: 0xff // 与ref 做与运算判断是否通过
},
op:{
fail:"keep",
zfail: "keep",
zpass: "replace",// increment 加+1
}
}
});
// 将颜色缓冲区的清掉
regl._gl.clear(regl._gl.COLOR_BUFFER_BIT);
// 只绘制与模板缓冲相交的地方
drawRect({
color:[0,1,0,1],
position:rectPosition,
modelMatrix:modelMatrix2,
// 设置是否可以写入颜色缓冲区
colorMask:[true,true,true,true],
stencil:{
enable:true,
mask:0xff,
func:{
cmp: 'equal',
ref: 4,
mask: 0xff //
},
op:{
fail:"keep",
zfail: "keep",
zpass: "keep",
}
}
})
// regl._gl.flush()
}
update()
}
}
})