Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ texture
# Downloaded files
examples/stb_image.h
examples/stb_image_write.h
*.d
19 changes: 19 additions & 0 deletions examples/raw/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Demos of raw usages

## `alpha.c`: alpha channel

Illustrates alpha channel support by rendering a PNG with the following shapes:

- **Bottom half**: A red opaque rectangle with three overlapping triangles at different alpha levels — green (α=0.8, nearly opaque), blue (α=0.5, half-transparent showing the red through), and cyan (α=0.25, mostly transparent)
- **Upper left**: A smooth-shaded magenta triangle that fades from fully opaque at the base to fully transparent at the tip — demonstrating per-vertex alpha interpolation
- **Upper right**: A checkerboard RGBA texture where the white squares fade from transparent (bottom) to opaque (top), and the golden squares have α=200/255 — demonstrating per-texel texture alpha

To run this demo:

```sh
cc -std=c99 -O2 -Iinclude -Iexamples examples/raw/alpha.c lib/libTinyGL.a -lm -o alpha && ./alpha
```

Then find `alpha_demo.png` in your working directory. Expected image:

<img width="512" height="512" alt="alpha_demo" src="https://github.com/user-attachments/assets/fa3ae5c0-961b-40a5-899d-6684a1403a94" />
200 changes: 200 additions & 0 deletions examples/raw/alpha.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* Alpha blending demo - renders to alpha_demo.png
*
* Scene: A red background with three overlapping triangles at different
* alpha levels, plus a textured quad with per-texel alpha.
*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <TGL/gl.h>
#include "zbuffer.h"

#define STBIW_ASSERT(x)
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

#define WIDTH 512
#define HEIGHT 512
#define TEX_SIZE 256

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

/* Generate a checkerboard RGBA texture with varying alpha */
static void make_checker_texture(unsigned char *rgba)
{
for (int y = 0; y < TEX_SIZE; y++) {
for (int x = 0; x < TEX_SIZE; x++) {
int idx = (y * TEX_SIZE + x) * 4;
int check = ((x / 32) + (y / 32)) & 1;
if (check) {
/* White squares, alpha varies by row */
rgba[idx + 0] = 255;
rgba[idx + 1] = 255;
rgba[idx + 2] = 255;
rgba[idx + 3] = (unsigned char)(255 * y / TEX_SIZE);
} else {
/* Yellow squares, full alpha */
rgba[idx + 0] = 255;
rgba[idx + 1] = 200;
rgba[idx + 2] = 0;
rgba[idx + 3] = 200;
}
}
}
}

int main(void)
{
ZBuffer *zb;
unsigned char *framebuf;

/* Initialize */
zb = ZB_open(WIDTH, HEIGHT, ZB_MODE_RGBA, NULL);
if (!zb) {
fprintf(stderr, "Failed to open ZBuffer\n");
return 1;
}
glInit(zb);

glViewport(0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);

/* Clear to dark blue background */
glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

/* Enable alpha blending */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

/* --- Draw a large opaque red rectangle as background element --- */
glShadeModel(GL_FLAT);
glColor4f(0.8f, 0.2f, 0.1f, 1.0f);
glBegin(GL_QUADS);
glVertex3f(-0.9f, -0.9f, 0.0f);
glVertex3f( 0.9f, -0.9f, 0.0f);
glVertex3f( 0.9f, -0.2f, 0.0f);
glVertex3f(-0.9f, -0.2f, 0.0f);
glEnd();

/* --- Three overlapping triangles with decreasing alpha --- */

/* Green triangle, alpha=0.8 (mostly opaque) */
glShadeModel(GL_FLAT);
glColor4f(0.0f, 0.9f, 0.2f, 0.8f);
glBegin(GL_TRIANGLES);
glVertex3f(-0.7f, -0.8f, 0.0f);
glVertex3f( 0.0f, -0.8f, 0.0f);
glVertex3f(-0.35f, 0.0f, 0.0f);
glEnd();

/* Blue triangle, alpha=0.5 (half transparent) */
glColor4f(0.2f, 0.3f, 1.0f, 0.5f);
glBegin(GL_TRIANGLES);
glVertex3f(-0.3f, -0.8f, 0.0f);
glVertex3f( 0.4f, -0.8f, 0.0f);
glVertex3f( 0.05f, 0.0f, 0.0f);
glEnd();

/* Cyan triangle, alpha=0.25 (mostly transparent) */
glColor4f(0.0f, 0.9f, 0.9f, 0.25f);
glBegin(GL_TRIANGLES);
glVertex3f( 0.1f, -0.8f, 0.0f);
glVertex3f( 0.8f, -0.8f, 0.0f);
glVertex3f( 0.45f, 0.0f, 0.0f);
glEnd();

/* --- Smooth-shaded triangle with alpha gradient --- */
glShadeModel(GL_SMOOTH);
glBegin(GL_TRIANGLES);
/* Fully opaque magenta at bottom-left */
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
glVertex3f(-0.8f, 0.1f, 0.0f);
/* Fully opaque magenta at bottom-right */
glColor4f(1.0f, 0.0f, 1.0f, 1.0f);
glVertex3f( 0.0f, 0.1f, 0.0f);
/* Fully transparent magenta at top (fades out) */
glColor4f(1.0f, 0.0f, 1.0f, 0.0f);
glVertex3f(-0.4f, 0.9f, 0.0f);
glEnd();

/* --- Textured quad with per-texel alpha (checkerboard) --- */
{
static unsigned char tex_data[TEX_SIZE * TEX_SIZE * 4];
GLuint tex;

make_checker_texture(tex_data);

glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, 4, TEX_SIZE, TEX_SIZE, 0,
GL_RGBA, GL_UNSIGNED_BYTE, tex_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glEnable(GL_TEXTURE_2D);
glShadeModel(GL_FLAT);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 0.1f, 0.1f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.9f, 0.1f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.9f, 0.9f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 0.1f, 0.9f, 0.0f);
glEnd();

glDisable(GL_TEXTURE_2D);
glDeleteTextures(1, &tex);
}

/* --- Save to PNG --- */
framebuf = malloc(WIDTH * HEIGHT * 3);
if (!framebuf) {
fprintf(stderr, "Out of memory\n");
ZB_close(zb);
glClose();
return 1;
}

/* Convert ARGB framebuffer to RGB for PNG */
{
unsigned int *pbuf = (unsigned int *)zb->pbuf;
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
unsigned int pix = pbuf[y * WIDTH + x];
int idx = (y * WIDTH + x) * 3;
framebuf[idx + 0] = (pix >> 16) & 0xFF; /* R */
framebuf[idx + 1] = (pix >> 8) & 0xFF; /* G */
framebuf[idx + 2] = pix & 0xFF; /* B */
}
}
}

if (stbi_write_png("alpha_demo.png", WIDTH, HEIGHT, 3, framebuf,
WIDTH * 3)) {
printf("Saved alpha_demo.png (%dx%d)\n", WIDTH, HEIGHT);
} else {
fprintf(stderr, "Failed to write PNG\n");
free(framebuf);
ZB_close(zb);
glClose();
return 1;
}

free(framebuf);
ZB_close(zb);
glClose();
return 0;
}
50 changes: 43 additions & 7 deletions include/zbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,16 @@ typedef GLushort PIXEL;
#endif

#if TGL_HAS(LIT_TEXTURES)
#if TGL_FEATURE_RENDER_BITS == 32
/* Preserve alpha from the texture pixel through the color mix */
#define RGB_MIX_FUNC(rr, gg, bb, tpix) \
(RGB_TO_PIXEL(((rr * GET_RED(tpix)) >> 8), ((gg * GET_GREEN(tpix)) >> 8), \
((bb * GET_BLUE(tpix)) >> 8)) | ((tpix) & 0xFF000000))
#else
#define RGB_MIX_FUNC(rr, gg, bb, tpix) \
RGB_TO_PIXEL(((rr * GET_RED(tpix)) >> 8), ((gg * GET_GREEN(tpix)) >> 8), \
((bb * GET_BLUE(tpix)) >> 8))
#endif
#else
#define RGB_MIX_FUNC(rr, gg, bb, tpix) (tpix)
#endif
Expand Down Expand Up @@ -162,23 +169,34 @@ typedef GLushort PIXEL;
break; \
}

/* Alpha blending helper: multiply 8.16 fixed-point color by [0,255] factor.
* Uses (factor + (factor >> 7)) to map [0,255] -> [0,256] so that
* factor=255 yields identity and factor=0 yields zero. */
#define TGL_ALPHA_MUL(val, factor) \
((((val) >> 16) * ((factor) + ((factor) >> 7))) << 8)

#define TGL_BLEND_FUNC(source, dest) \
{{GLuint sr, sg, sb, dr, dg, db; \
{{GLuint sr, sg, sb, dr, dg, db, sa; \
{ \
GLuint temp = source; \
sr = GET_REDDER(temp); \
sg = GET_GREENER(temp); \
sb = GET_BLUEER(temp); \
sa = (temp >> 24) & 0xFF; \
temp = dest; \
dr = GET_REDDER(temp); \
dg = GET_GREENER(temp); \
db = GET_BLUEER(temp); \
} \
/*printf("\nShould never reach this point!");*/ \
switch (sfactor) { \
case GL_ONE: \
default: \
break; \
case GL_SRC_ALPHA: \
sr = TGL_ALPHA_MUL(sr, sa); \
sg = TGL_ALPHA_MUL(sg, sa); \
sb = TGL_ALPHA_MUL(sb, sa); \
break; \
case GL_ONE_MINUS_SRC_COLOR: \
sr = ~sr & COLOR_MASK; \
sg = ~sg & COLOR_MASK; \
Expand All @@ -194,6 +212,12 @@ typedef GLushort PIXEL;
case GL_ONE: \
default: \
break; \
case GL_ONE_MINUS_SRC_ALPHA: \
{GLuint inv_sa = 255 - sa; \
dr = TGL_ALPHA_MUL(dr, inv_sa); \
dg = TGL_ALPHA_MUL(dg, inv_sa); \
db = TGL_ALPHA_MUL(db, inv_sa);} \
break; \
case GL_ONE_MINUS_DST_COLOR: \
dr = ~dr & COLOR_MASK; \
dg = ~dg & COLOR_MASK; \
Expand All @@ -209,20 +233,25 @@ typedef GLushort PIXEL;
} \
}

#define TGL_BLEND_FUNC_RGB(rr, gg, bb, dest) \
#define TGL_BLEND_FUNC_RGB(rr, gg, bb, aa, dest) \
{{GLint sr = rr & COLOR_MASK, sg = gg & COLOR_MASK, sb = bb & COLOR_MASK, \
dr, dg, db; \
GLuint sa = ((aa) >> 16) & 0xFF; \
{ \
GLuint temp = dest; \
dr = GET_REDDER(temp); \
dg = GET_GREENER(temp); \
db = GET_BLUEER(temp); \
} \
/*printf("\nShould never reach this point!");*/ \
switch (sfactor) { \
case GL_ONE: \
default: \
break; \
case GL_SRC_ALPHA: \
sr = TGL_ALPHA_MUL(sr, sa); \
sg = TGL_ALPHA_MUL(sg, sa); \
sb = TGL_ALPHA_MUL(sb, sa); \
break; \
case GL_ONE_MINUS_SRC_COLOR: \
sr = ~sr & COLOR_MASK; \
sg = ~sg & COLOR_MASK; \
Expand All @@ -238,6 +267,12 @@ typedef GLushort PIXEL;
case GL_ONE: \
default: \
break; \
case GL_ONE_MINUS_SRC_ALPHA: \
{GLuint inv_sa = 255 - sa; \
dr = TGL_ALPHA_MUL(dr, inv_sa); \
dg = TGL_ALPHA_MUL(dg, inv_sa); \
db = TGL_ALPHA_MUL(db, inv_sa);} \
break; \
case GL_ONE_MINUS_DST_COLOR: \
dr = ~dr & COLOR_MASK; \
dg = ~dg & COLOR_MASK; \
Expand All @@ -259,9 +294,9 @@ typedef GLushort PIXEL;
{ \
dest = source; \
}
#define TGL_BLEND_FUNC_RGB(rr, gg, bb, dest) \
{ \
dest = RGB_TO_PIXEL(rr, gg, bb); \
#define TGL_BLEND_FUNC_RGB(rr, gg, bb, aa, dest) \
{ \
dest = RGB_TO_PIXEL(rr, gg, bb); \
}
#endif

Expand Down Expand Up @@ -303,6 +338,7 @@ typedef struct {
GLint x, y, z; /* integer coordinates in the zbuffer */
GLint s, t; /* coordinates for the mapping */
GLint r, g, b; /* color indexes */
GLint a; /* alpha (8.16 fixed-point, same format as r,g,b) */

GLfloat sz, tz; /* temporary coordinates for mapping */
} ZBufferPoint;
Expand Down
Loading