惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
O
OpenAI News
W
WeLiveSecurity
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Heimdal Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
T
Threatpost
T
Troy Hunt's Blog
T
Tenable Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
Forbes - Security
Forbes - Security
Cyberwarzone
Cyberwarzone
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
腾讯CDC
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
D
Docker
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
I
InfoQ
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
L
LINUX DO - 热门话题
The Cloudflare Blog
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
小众软件
小众软件

WebGL Fundamentals

WebGL Using 2 or More Textures WebGL Implementing DrawImage WebGL 2D Matrices WebGL Implementing A Matrix Stack WebGL 2D Rotation WebGL 2D Scale WebGL 2D Translation WebGL - Rasterization vs 3D libraries WebGL 3D - Cameras WebGL 3D Geometry - Lathe WebGL 3D - Directional Lighting WebGL 3D - Point Lighting WebGL 3D - Normal Mapping WebGL 3D - Spot Lighting WebGL - Orthographic 3D WebGL 3D Perspective Correct Texture Mapping WebGL 3D Perspective WebGL Textures WebGL and Alpha WebGL - Animation WebGL Anti-Patterns WebGL Attributes WebGL Boilerplate WebGL - Cross Origin Images WebGL Cross Platform Issues WebGL Cubemaps WebGL 3D - Data Textures WebGL - Drawing Multiple Things WebGL Drawing Without Data WebGL Environment Maps (reflections) WebGL Fog WebGL Framebuffers WebGL Fundamentals WebGL GPGPU WebGL How It Works WebGL Image Processing Continued WebGL Image Processing WebGL Indexed Vertices WebGL Optimization - Instanced Drawing WebGL - Less Code, More Fun WebGL Load Obj with Mtl WebGL Load Obj WebGL Matrices vs Math Matrices WebGL Multiple Views, Multiple Canvases WebGL Picking WebGL Planar and Perspective Projection Mapping WebGL Points, Lines, and Triangles WebGL Post Processing WebGL Precision Issues WebGL Pulling Vertices Accessing textures by pixel coordinate in WebGL2 A simple way to show the load on the GPU's vertex and fragment processing? Apply a displacement map and specular map Can anyone explain what this GLSL fragment shader is doing? Can I mute the warning about vertex attrib 0 being disabled? Create image warping effect in WebGL Creating a smudge/liquify effect How to draw Depth Sprites Determine min/max values for the entire image Don't blend a polygon that crosses itself Drawing 2D image with depth map to achieve pseudo-3D effect Drawing a heightmap Drawing layers with different points Drawing Many different models in a single draw call Drawing textured sprites with instanced drawing Efficient particle system in javascript? (WebGL) Emulating palette based graphics in WebGL FPS-like camera movement with basic matrix transformations Get the size of a point for collision checking GLSL shader to support coloring and texturing How can I compute for 500 points which of 1000 line segments is nearest to each point? How can I create a 16bit historgram of 16bit data How can I get all the uniforms and uniformBlocks How can I move the perspective vanishing point from the center of the canvas? How to Achieve Moving Line with Trail Effects How to bind an array of textures to a WebGL shader uniform? How to blend colors across 2 triangles How to combine more text drawing into fewer draw calls How to control the color between vertices How to create a torus How to detect clipped triangles in the framgment shader How to determine the average brightness in a scene? How to draw correctly textured trapezoid polygons How to fade the drawing buffer How to figure out how much GPU work to do without crashing WebGL How to get audio data into a shader How to get code completion for WebGL in Visual Studio Code How to get the 3d coordinates of a mouse click How to get pixelize effect in webgl? How to implement zoom from mouse in 2D WebGL How to import a heightmap in WebGL How to load images in the background with no jank How to make a smudge brush tool How to make WebGL canvas transparent How to optimize rendering a UI How to prevent texture bleeding with a texture atlas How to process particle positions How to read a single component with readPixels How to render large scale images like 32000x32000 How to simulate a 3D texture in WebGL
WebGL Smallest Programs
WebGLFundame · 2025-02-26 · via WebGL Fundamentals

This article assumes you've read many of the other articles starting with the fundamentals. If you have not read them please start there first.

I don't really know what to file this article under because it has two purposes.

  1. Show you the smallest WebGL programs.

    These techniques are super useful for testing something or when making an MCVE for Stack Overflow or when trying to narrow down a bug.

  2. Learning to think outside the box

    I hope to write several more articles on this to help you see the bigger picture rather than just the common patterns. Here's one.

Just clearing

Here's the smallest WebGL program that actually does something

const gl = document.querySelector('canvas').getContext('webgl');
gl.clearColor(1, 0, 0, 1);  // red
gl.clear(gl.COLOR_BUFFER_BIT);

All this program does is clear the canvas to red but it did actually do something.

Think about it through. With just this you can actually test some things. Let's say you are rendering to a texture but things aren't working. Let's say it's just like the example in that article. You're rendering 1 or more 3D things into a texture then rendering that result onto a cube.

You're not seeing anything. Well, as a simple test, stop rendering to the texture with shaders are just clear the texture to a known color.

gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferWithTexture)
gl.clearColor(1, 0, 1, 1);  // magenta
gl.clear(gl.COLOR_BUFFER_BIT);

Now render with the texture from the framebuffer. Does your cube turn magenta? If not then your issue is not the rendering to the texture part it's something else.

Using the SCISSOR_TEST and gl.clear

The SCISSOR_TEST clips both drawing and clearing to some sub rectangle of the canvas (or current framebuffer).

You enable the scissor test with

gl.enable(gl.SCISSOR_TEST);

and then you set the scissor rectangle in pixels relative to the bottom left corner. It uses the same parameters as gl.viewport.

gl.scissor(x, y, width, height);

Using that can draw rectangles using the SCISSOR_TEST and gl.clear.

Example

const gl = document.querySelector('#c').getContext('webgl');

gl.enable(gl.SCISSOR_TEST);

function drawRect(x, y, width, height, color) {
  gl.scissor(x, y, width, height);
  gl.clearColor(...color);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

for (let i = 0; i < 100; ++i) {
  const x = rand(0, 300);
  const y = rand(0, 150);
  const width = rand(0, 300 - x);
  const height = rand(0, 150 - y);
  drawRect(x, y, width, height, [rand(1), rand(1), rand(1), 1]);
}


function rand(min, max) {
  if (max === undefined) {
    max = min;
    min = 0;
  }
  return Math.random() * (max - min) + min;
}

Not saying that particular one is all that useful but still it's good to know.

Using one large gl.POINTS

As most of the examples show, the most common thing to do in WebGL is create buffers. Put vertex data in those buffers. Create shaders with attributes. Set up the attributes to pull data from those buffers. Then draw, possibly with uniforms and texture also used by your shaders.

But sometimes you just want to test. Let's say you want just see something draw.

How about this set of shaders

// vertex shader
void main() {
  gl_Position = vec4(0, 0, 0, 1);  // center
  gl_PointSize = 120.0;
}
// fragment shader
precision mediump float;

void main() {
  gl_FragColor = vec4(1, 0, 0, 1);  // red
}

And here's the code to use it

// setup GLSL program
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);

gl.useProgram(program);

const offset = 0;
const count = 1;
gl.drawArrays(gl.POINTS, offset, count);

No buffers to create, no uniforms to setup, and we get a single point in the middle of the canvas.

NOTE: Safari pre 15 didn't pass the WebGL Conformance Tests for this feature.

About gl.POINTS: When you pass gl.POINTS to gl.drawArrays you're also required to set gl_PointSize in your vertex shader to a size in pixels. It's important to note that different GPU/Drivers have a different maximum point size you can use. You can query that maximum size with

const [minSize, maxSize] = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE);

The WebGL spec only requires a max size of 1.0. Fortunately most if not all GPUs and drivers support a larger size.

After you set gl_PointSize then when the vertex shader exits, whatever value you set on gl_Position is converted to screen/canvas space in pixels, then a square is generated around that position that is +/- gl_PointSize / 2 in all 4 directions.

Okay, I can hear you thinking so what, who wants to draw a single point.

Well, points automatically get free texture coordinates. They are available in the fragment shader with the special variable gl_PointCoord. So, let's draw a texture on that point.

First let's change the fragment shader.

// fragment shader
precision mediump float;

+uniform sampler2D tex;

void main() {
-  gl_FragColor = vec4(1, 0, 0, 1);  // red
+  gl_FragColor = texture2D(tex, gl_PointCoord.xy);
}

Now to keep it simple let's make a texture with raw data like we covered in the article on data textures.

// 2x2 pixel data
const pixels = new Uint8Array([
  0xFF, 0x00, 0x00, 0xFF,  // red
  0x00, 0xFF, 0x00, 0xFF,  // green
  0x00, 0x00, 0xFF, 0xFF,  // blue
  0xFF, 0x00, 0xFF, 0xFF,  // magenta
]);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
    gl.TEXTURE_2D,
    0,                 // level
    gl.RGBA,           // internal format
    2,                 // width
    2,                 // height
    0,                 // border
    gl.RGBA,           // format
    gl.UNSIGNED_BYTE,  // type
    pixels,            // data
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

Because WebGL defaults to using texture unit 0 and because uniforms default to 0 there is nothing else to setup

This can be a great way to test texture related problems. We're still using no buffers, no attributes, and we didn't have to look up and set any uniforms. For example if we loaded an image, it's not showing up. What if we try the shader above, does it show the image on the point? We rendered to a texture and then we want to view the texture. Normally we'd setup some geometry via buffers and attributes but we can render the texture just by showing it on this single point.

Using Multiple Single POINTS

Another simple change to the example above. We can change the vertex shader to this

// vertex shader

+attribute vec4 position;

void main() {
-  gl_Position = vec4(0, 0, 0, 1);
+  gl_Position = position;
  gl_PointSize = 120.0;
}

attributes have a default value of 0, 0, 0, 1 so with just that change the examples above would still continue to work. But, now we gain the ability to set the position if we want.

+const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');

...

+const numPoints = 5;
+for (let i = 0; i < numPoints; ++i) {
+  const u = i / (numPoints - 1);    // 0 to 1
+  const clipspace = u * 1.6 - 0.8;  // -0.8 to +0.8
+  gl.vertexAttrib2f(positionLoc, clipspace, clipspace);

*  const offset = 0;
*  const count = 1;
*  gl.drawArrays(gl.POINTS, offset, count);
+}

Before we run it lets make the point smaller

// vertex shader

attribute vec4 position;
+uniform float size;

void main() {
  gl_Position = position;
-  gl_PointSize = 120.0;
+  gl_PointSize = 20.0;
}

And lets make it so we can set the color of the point. (note: I switched back to the code without a texture).

precision mediump float;

+uniform vec4 color;

void main() {
-  gl_FragColor = vec4(1, 0, 0, 1);   // red
+  gl_FragColor = color;
}

and we need to lookup the color location

// setup GLSL program
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
+const colorLoc = gl.getUniformLocation(program, 'color');

And use them

gl.useProgram(program);

const numPoints = 5;
for (let i = 0; i < numPoints; ++i) {
  const u = i / (numPoints - 1);    // 0 to 1
  const clipspace = u * 1.6 - 0.8;  // -0.8 to +0.8
  gl.vertexAttrib2f(positionLoc, clipspace, clipspace);

+  gl.uniform4f(colorLoc, u, 0, 1 - u, 1);

  const offset = 0;
  const count = 1;
  gl.drawArrays(gl.POINTS, offset, count);
}

And now we get 5 points with 5 colors and we still didn't have to setup any buffers or attributes.

Of course this is NOT the way you should draw lots of points in WebGL. If you want to draw lots of points you should do something like setup an attribute with a position for each point, and a color for each point and draw all the points in a single draw call.

BUT!, for testing, for debugging, for making an MCVE it's a great way to minimize the code. As another example let's say we're drawing to textures for a post processing affect and we want to visualize them. We could just draw one large point for each one using the combination of this example and the previous one with a texture. No complicated step of buffers and attributes needed, great for debugging.