How to emboss and put over the texture


I’m looking for a way to gently emboss an element and render the result over existing texture.

So my wish is for this red blob


to become this


Should I use shader or there something other way to achieve this ?
I’ve seen that there are few emboss shaders on shadertoy that I probably should try, but the problem I’m having with shadertoy is that it draws result on black background and never on transparent.

Many thanks

so I’ve found an emboss shader.

here is what I give him as texture

here is what I get back

however what I need to get is this…

any hints on how achieve this ?


Hey @SFR75 , first make your are using GL_RGBA when allocating an ofFbo, which you probably are and it is the default format. Then if the texture always has a green background, you can use this in frag shader to set the alpha channel

vec4 color = texture(tex0, tc);
color.a = step(1.0, color.g); // sets .a to 0 if .g < 1.0
// emboss functions then modify color.rgb

Alternatively, you can run the texture thru a quick shader that just sets the alpha channel if you need to, so that the texture going into the emboss shader has the alphas all set.

Also in case it helps, the emboss falls into a category of kernel or convolution shaders, which are all kinda the same except they vary by the values in the matrix. Some other examples are sharpen, Sobel, blur, etc.

Hello TimChi,

Ok, thanks. I will give it a try.
What’s a bit confusing is that shadertoy contains these hardcoded lines:

“void main( void ) {”
" vec4 color = vec4(0.0,0.0,0.0,1.0);"
" mainImage( color, gl_FragCoord.xy );"
" color.w = 1.0;"
" FragColor = color;"

if I understand correctly color.w = 1.0 sets alpha to white making everything non-transparent ?

Yeah color.w = 1.0 will set the alpha channel to 1.0, which is opaque. Transparent is color.w = 0.0. So you’d have to modify the color.w depending on the background (or some other calculation) at some point before FragColor = color.

There may be too many variables called color at the moment. How about:

// a new vec4; use it to read the color from the texture before emboss stuff
vec4 textureColor = texture(tex0, tc);
// use textureColor to get a new float opacity;
// opacity will be 0.0 if textureColor.g is 1.0 (or greater), and 0.0 if textureColor.g < 1.0
float opacity = step(1.0, textureColor.g);
// this color comes from the Shadertoy shader, prob used to store the emboss color
vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
// calc the emboss color.rgb
// then right before setting FragColor, set color.w to opacity for above:
color = vec4(color.rgb, opacity);
FragColor = color;

Hello TimChi

Tried many things, but doesn’t work.

here is my code with your suggested changes:

// Source:

#define EMBOSS_WIDTH	0.0015
#define EMBOSS_HEIGHT	0.0015

// samples a pixel centerd at "uv" and with offset of dx|dy
vec4 sample_pixel(in vec2 uv, in float dx, in float dy)
    return texture(iChannel0, uv + vec2(dx, dy));

// convolves a SINGLE channel of input color_matrix
float convolve(in float[9] kernel, in vec4[9] color_matrix)
   float res = 0.0;
   for (int i=0; i<9; i++)
      res += kernel[i] * color_matrix[i].a;
   return clamp(res + 0.5, 0.0 ,1.0);

// builds a 3x3 color matrix centerd at "uv"
void build_color_matrix(in vec2 uv, out vec4[9] color_matrix)
    float dxtex = EMBOSS_WIDTH;
    float dytex = EMBOSS_HEIGHT;

	color_matrix[0].rgb = sample_pixel(uv, -dxtex, -dytex)	.rgb;
	color_matrix[1].rgb = sample_pixel(uv, -dxtex, 	0.0)	.rgb;
    color_matrix[2].rgb = sample_pixel(uv, -dxtex, 	dytex)	.rgb;
	color_matrix[3].rgb = sample_pixel(uv, 0.0, 	-dytex)	.rgb;
	color_matrix[4].rgb = sample_pixel(uv, 0.0, 	0.0)	.rgb;
    color_matrix[5].rgb = sample_pixel(uv, 0.0, 	dytex)	.rgb;
	color_matrix[6].rgb = sample_pixel(uv, dxtex, 	-dytex)	.rgb;
	color_matrix[7].rgb = sample_pixel(uv, dxtex, 	0.0)	.rgb;
    color_matrix[8].rgb = sample_pixel(uv, dxtex, 	dytex)	.rgb;

// builds a mean color matrix (off of .rgb of input).
// NOTE: stores the output in alpha channel
void build_mean_matrix(inout vec4[9] color_matrix)
   for (int i=0; i<9; i++)
      color_matrix[i].a = (color_matrix[i].r + color_matrix[i].g + color_matrix[i].b) / 3.;

void mainImage( out vec4 fragColor, in vec2 fragCoord )
    /*	2.	0.	0.
		0.	-1	0.
		0.	0.	-1	*/
    float kerEmboss[9];
    kerEmboss[0] = 2.0;
    kerEmboss[1] = 0.0;
    kerEmboss[2] = 0.0;
    kerEmboss[3] = 0.0;
    kerEmboss[4] = -1.;
    kerEmboss[5] = 0.0;
    kerEmboss[6] = 0.0;
    kerEmboss[7] = 0.0;
    kerEmboss[8] = -1.;
    vec4 pixel_matrix[9];
	vec2 uv = fragCoord.xy / iResolution.xy;
    build_color_matrix(uv, pixel_matrix);
    float convolved = convolve(kerEmboss, pixel_matrix);    
   	vec4 textureColor = texture(iChannel0, uv);
    float opacity = step(1.0, textureColor.g);
	fragColor = vec4(vec3(convolved).rgb, opacity);


I still get gray background and no alpha… :frowning:

crazy - such simple thing seems so complicated… :-/

what is very strange is that if I change last line to

fragColor = vec4(vec3(convolved).rgb, 0.0);

it makes no difference…

btw: could it be because of this: ?

// builds a mean color matrix (off of .rgb of input).
// NOTE: stores the output in alpha channel
void build_mean_matrix(inout vec4[9] color_matrix)



As a troubleshooting step, you can see what the value of opacity is by making a gray color out of it:

    vec3 textureColor = texture(iChannel0, uv).rgb;
    float opacity = 1.0 - step(0.6, textureColor.g); // changed this line
    fragColor = vec4(vec3(opacity), 1.0);  // see opacity as a gray color
	//fragColor = vec4(vec3(convolved), opacity);

On the Shadertoy shader, I got a better result by changing the threshold of the step() to 0.6. And then I subtracted from 1.0 to “flip” the values so that opacity was 0.0 for the green color; but this was a surprise.

So with a green and purple texture, a lower threshold value might be better unless you know for certain that the green channel will be 1.0 for anything you want to be transparent. And remember that whites will have high values in the green channel too.

Also, instead of step, you can use an if:

float opacity = 1.0;
if(thresholdColor.g > 0.6){
    opacity = 0.0;

I like using ifs better, but step() is more common and more glsl-ish. I always thought step() was 0.0 below the threshold and 1.0 above it, but apparently this is not the case with step() on Shadertoy.

Hello TimChi

Thanks for your answer. So if I understand correctly the problem is with step() function ?
I will try your code tomorrow.

Regarding the background, yes it’s pure green (0,255,0). I fill my fbo with this color and then draw the blob I want to emboss.


Hey I was hoping this was the case!

I’m not sure where the problem is. However, fragColor = vec4(vec3(opacity), 1.0); will be white when opacity is 1.0, and black when it’s 0.0. The emboss shader looks like it’s just calculating the RGB values and not affecting the opacity.

In an OF shader with the following, value is 0.0 when uv.x < 0.5, and value is 1.0 when uv.x > 0.5, and (I’m pretty sure that) value is 1.0 when uv.x == 0.5:

float value = step(0.5, uv.x);

One additional thing to keep in mind is whether or not the texture coordinates are normalized or not, and sample accordingly (even if uv is normalized for other purposes).

I think it’s OK if build_mean_matrix() stores a value in the alpha channel. The alpha channel of fragColor is set when the vec4 is created. You should be able to “see” differences amongst the following:

fragColor = vec4(vec3(convolved).rgb, 0.0);
fragColor = vec4(vec3(convolved).rgb, 1.0);
fragColor = vec4(vec3(convolved).rgb, uv.x); // increasing opacity left to right

I don’t think the checkerboard pattern appears for transparent areas. But you can draw the fbo over something else to see the alpha blending effect

yes, I do draw fbo over background and I checked with other alpha image, it works.

however with shader it’s always gray no matter what.

I tried those and they make no difference…

fragColor = vec4(vec3(convolved).rgb, 0.0);
fragColor = vec4(vec3(convolved).rgb, 1.0);
fragColor = vec4(vec3(convolved).rgb, uv.x);

Looks like it was because of this line in Shadertoy.cpp

now I get this (halftone pattern is bg)

Hey so is working? Is the shader drawing an opaque embossed blob with a transparent background over an opaque halftone pattern? I hope its working.

Yes the line color.w = 1.0; would make every pixel opaque if fragColor = color; is the last line of that shader and sets the out vec4 of the fragment shader.

One thing to consider would be to ofClear() with a transparent color before drawing the blob, and then just use the alpha channel instead of the green channel to set the alpha values. This might help the shader be more accommodating to background colors that are not green. Transparent gray would be ofClear(ofColor(127, 127, 127, 0)).


Yes it is working. It looks like this line permanently disabled alpha in shadertoy. Don’t know what was the reason behind this decision. Unfortunately all this was useless - I can’t use emboss for different reason (compositing).

Thanks for your help!
and happy new year!

1 Like