我正在开发一个在WebGL中绘制圆的着色器,它似乎可以工作,但我不确定如何工作。这是顶点着色器:
in vec2 a_unit;
uniform mat3 u_projection;
uniform vec3 u_transform;
out vec2 v_pos;
void main() {
v_pos = a_unit;
float r = u_transform.z;
float x = u_transform.x - r;
float y = u_transform.y - r;
float w = r * 2.0;
float h = r * 2.0;
mat3 world = mat3(
w, 0, 0,
0, h, 0,
x, y, 1
);
gl_Position = vec4(u_projection * world * vec3(a_unit, 1), 1);
}
这是碎片着色器:
in vec2 v_pos;
uniform vec4 u_color;
uniform vec3 u_transform;
uniform mat3 u_projection;
out vec4 outputColor;
void main() {
vec2 dist = v_pos - 0.5;
if (dist.x * dist.x + dist.y * dist.y > .222) {
discard;
}
outputColor = u_color;
}
我向着色器传递一个u_projection
矩阵,它只是我的相机。我还传递了一个u_transform
向量,它就是我的圆的世界坐标(和世界半径(。
例如,u_transform
可能类似于(200, 300, 25)
,用于用radius: 25
在x: 200
、y: 300
处绘制圆
哦,a_unit
只是一个四单元:
[
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
]
逻辑是,我只画一个矩形,然后通过丢弃不在半径内的任何东西来剪裁。于是画了一个圆。
我的困惑在于这一行:
if (dist.x * dist.x + dist.y * dist.y > .222) { discard; }
我通过反复猜测和检查得出了0.222
的值。我不明白它是怎么工作的。为什么这个神奇的数字能画一个圆圈?
我想我需要将radius
转换为纹理坐标,但这似乎不是必需的?但我不知道为什么。。
为什么这能画出一个任意半径的圆?什么是合适的值,因为0.222
虽然很接近,但不是确切的值。我应该在这里使用什么值来代替0.222
?
您可以尝试以下代码:(这是一个新代码(:
onload = function(){
const canvas = document.querySelector("canvas")
var gl = canvas.getContext("webgl")
const vertexShaderCode = document.getElementById("vertexShaderCode").innerHTML
const fragmentShaderCode = document.getElementById("fragmentShaderCode").innerHTML
var vertices = []
var x , y , x1 , y1 , radius = 0.5 , startX = 0 , startY = 0
x1 = Math.cos(0) * radius
y1 = Math.sin(0) * radius
for(var degree=0; degree<=6.3; degree+=0.1){
x = Math.cos(degree) * radius
y = Math.sin(degree) * radius
vertices.push(startX + x1)
vertices.push(startY + y1)
vertices.push(startX)
vertices.push(startY)
vertices.push(startX + x)
vertices.push(startY + y)
x1 = x
y1 = y
}
var vertexShader = gl.createShader(gl.VERTEX_SHADER)
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader , vertexShaderCode)
gl.shaderSource(fragmentShader, fragmentShaderCode)
gl.compileShader(vertexShader)
gl.compileShader(fragmentShader)
if(!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)){
console.error(`ERROR::Compiling vertex shader , n ${gl.getShaderInfoLog(vertexShader)}`)
}
if(!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)){
console.error(`ERROR::Compiling fragment shader , n ${gl.getShaderInfoLog(fragmentShader)}`)
}
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
if(!gl.getProgramParameter(program, gl.LINK_STATUS)){
console.error(`ERROR::Linking current program , n ${gl.getProgramInfoLog(program)}`)
}
gl.validateProgram(program)
if(!gl.getProgramParameter(program, gl.VALIDATE_STATUS)){
console.error(`ERROR::validating current program , n ${gl.getProgramInfoLog(program)}`)
}
gl.useProgram(program)
var buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
var positionAttribLocation = gl.getAttribLocation(program, "pos")
gl.vertexAttribPointer(
positionAttribLocation ,
2,
gl.FLOAT,
false,
2*Float32Array.BYTES_PER_ELEMENT,
0*Float32Array.BYTES_PER_ELEMENT
)
gl.enableVertexAttribArray(positionAttribLocation)
function loop(){
requestAnimationFrame(loop)
canvas.width = innerWidth
canvas.height = innerHeight
gl.viewport(0,0,canvas.width,canvas.height)
gl.clearColor(0,0,0,1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, vertices.length/2)
}
requestAnimationFrame(loop)
}
body {
margin:0;
width:100vw;
height:100vh;
overflow:hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Circle with WebGL</title>
</head>
<body>
<canvas>Your browser does not support HTML5 canvas !</canvas>
<script id="vertexShaderCode" type="x-shader/x-vertex">precision mediump float;
attribute vec2 pos;
void main(){
gl_Position = vec4(pos, 0.0, 1.0);
}
</script>
<script id="fragmentShaderCode" type="x-shader/x-fragment">precision mediump float;
void main(){
gl_FragColor = vec4(1.0 , 0.0 , 0.0 , 1.0);
}
</script>
</body>
</html>