DUDSS
DUDSS

Reputation: 130

How to replicate the photoshop bevel/emboss border effect in glsl

I've been trying to find some information about how one would replicate the photoshop bevel effect with a GLSL shader.

Example of the effect in PS

I've found some example shaders but I can't seem to wrap my head around it. I've stumbled across this question https://dsp.stackexchange.com/questions/530/bitmap-alpha-bevel-algorithm, which is achieving the desired result but I don't know how to translate that into a shader.

Any advice would be appreciated.

Upvotes: 0

Views: 1216

Answers (1)

kolenda
kolenda

Reputation: 2811

First, I'll warn you that this may get tricky to fit into a shader, depending on your GPU and desired quality but you can give it a try.

I'd split this problem into two separate ones:

    1. Find distance and direction to the closest hole.
    1. Shade pixel based on distance/direction.

Ad. 1.

You want to find the nearest pixel that has alpha below some threshold. You test the pixel your shader starts with, then you test its neighbors (3x3 grid, without center), then their neighbors (5x5) and so on... You continue this until you've found a pixel or you exceeded the size of your bevel.

This is the tricky part. There's not much math going on but you have A LOT of texture reads and if-else which GPUs don't like. When you increase your border size the performance goes down very fast. GPUs seem to have some kind of 'timeout' functionality when executing shaders, so even perfectly working one may get silently killed if it takes too long. Those limits may differ between GPUs and/or drivers, so it's hard to say if the code that works on your machine will work on another. This is hard to test for sure but can be done.

Ad. 2.

With distance and direction you're almost done. If the distance is bigger than border size then current pixel is not a border, otherwise you compare your direction with some light-dir (it was up-left on your example) and decide if border should be lighter or darker. You may also use distance to influence light/dark strenght so you could get rounded slopes instead of flat ones.

All of this is just a simplest approach. You can try to split it into render passes, do some precomputations, downscale textures, use mip-maps as quad trees, etc. to speed up the code.

EDIT For your comment:

As long as you don't plan to modify the data per frame you can precompute a lot.

Let's say you have a texture where you holes are white and the rest is black and you want to soften this image. You write a shader that samples current pixel (let's call it pix) and samples its 8 neighbors and averages them (let's call it avg), then you call:

result = max( pix, avg );

This way you'll smear the white color around, but never darken pixels that are already white. You run this shader few times in a row to get bigger smear. Now, if you invert colors you'll get something like a distance values, right :) ?

(It can be done without inversion, you use alpha channel and min instead of max, I just felt this way is easier to describe).

The shape of this smear will depend on the pattern of your samples for avg value. I told you to take 3x3 grid but you can experiment here and use more round shapes.

A gradient of this distance value will give you a direction.

You can get even further and precompute your shading data. You can compute texture with values to be added or substracted for every pixel. Pack this texture into 0-255 range. Then, during your rendering you'd sample this texture, subtract 0.5 from it (to move to the [-0.5,0.5] range) and add to your source texture.

Upvotes: 1

Related Questions