# sharing is fun – Math Surface Shader

Hi folks,

Here’s a shader and patch I made following the "Calculate Normals In

Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

quite easy to plug in other equations from

http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

This one implements the Tranguloid Trefoil equation:

http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

wes

Nice one :)

I just picked up the Orange Book today, its been super helpful

already. Ive been able to properly antialias some of the procedural

texture generators ive made.

Might be interesting to put in two formulas and add mix() to morph

between vertex targets. Hmmm :)

On Sep 13, 2007, at 9:39 PM, Wesley Smith wrote:

> Hi folks,

> Here’s a shader and patch I made following the "Calculate Normals In

> Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

> quite easy to plug in other equations from

> http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

>

> This one implements the Tranguloid Trefoil equation:

> http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

>

>

> wes

v a d e //

http://www.vade.info

abstrakt.vade.info

That’s kind of what electric sheep is doing with iterated function

systems. That system works by having 14 equations each with its own

weight where they all add up to 1. It morphs between functions by

changing relative weights. The only trouble with doing that in a

vertex shader is how many equations th shader can take before it

becomes slow. I bet it could handle 3 on a good card, maybe more if

they’re not very complicated.

wes

On 9/13/07, vade

> Nice one :)

>

> I just picked up the Orange Book today, its been super helpful already. Ive

> been able to properly antialias some of the procedural texture generators

> ive made.

>

> Might be interesting to put in two formulas and add mix() to morph between

> vertex targets. Hmmm :)

>

>

> On Sep 13, 2007, at 9:39 PM, Wesley Smith wrote:

>

> Hi folks,

> Here’s a shader and patch I made following the "Calculate Normals In

> Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

> quite easy to plug in other equations from

> http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

>

> This one implements the Tranguloid Trefoil equation:

> http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

>

>

> wes

>

> v a d e //

>

> http://www.vade.info

> abstrakt.vade.info

>

>

>

>

>

>

I was actually thinking of staggering it, so each shader has 2

forumals in it, weighted between 0 and 1,

so you have shader "A" which has 1,2, shader b has 2 and 3, shader

c forumals 3 and 4

when you go from 1 to 2, you can then switch to shader b, go form 2

to 3, then to shader c which has 3 to 4 ?

I dunno, does having the shader uploaded but not bound waste GPU

cycles? I dont think so right?

Might be a solution :)

Either way, very cool.

erm, formulas. Has someone been spending too many hours on forum-

las ? *sigh* :)

That’s cool, Wes. I’m gonna definitely play around with this. And

thanks for sharing that blog. Some cool stuff there.

AB

I was thinking something similar as well but with 3 instead of 2 but 2

might be just as good with careful mixing. Doing it this way

shouldn’t be a problem at all GPU cycle-wise.

wes

this looks cool, but i’m shocked and saddened about how slow it is on

my 1.67g4 albook. is that a surprise? it feels like 5fps.

On Sep 13, 2007, at 9:39 PM, Wesley Smith wrote:

> Hi folks,

> Here’s a shader and patch I made following the "Calculate Normals In

> Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

> quite easy to plug in other equations from

> http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

>

> This one implements the Tranguloid Trefoil equation:

> http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

>

>

> wes

>

Try reducing the dimensions of the input matrix. I set it quite high.

BTW, what graphics card do you have? Radeon 9700? If so all of the

trigonometric stuff might kill it and if the shape takes up large

portion of the screen, the per-pixel lighting won’t help either.

wes

On 9/13/07, joshua goldberg

> this looks cool, but i’m shocked and saddened about how slow it is on

> my 1.67g4 albook. is that a surprise? it feels like 5fps.

>

>

> On Sep 13, 2007, at 9:39 PM, Wesley Smith wrote:

>

> > Hi folks,

> > Here’s a shader and patch I made following the "Calculate Normals In

> > Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

> > quite easy to plug in other equations from

> > http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

> >

> > This one implements the Tranguloid Trefoil equation:

> > http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

> >

> >

> > wes

> >

>

>

lower the vert count. its currently at 100 ?

On Sep 13, 2007, at 11:46 PM, joshua goldberg wrote:

> this looks cool, but i’m shocked and saddened about how slow it is

> on my 1.67g4 albook. is that a surprise? it feels like 5fps.

v a d e //

http://www.vade.info

abstrakt.vade.info

hi wes!

that

Hi Didi,

Here’s your patch as a shader. Not quite as smooth as nurbs but still nice.

wes

#P window setfont "Sans Serif" 9.;

#P flonum 720 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P window linecount 1;

#P message 720 247 59 196617 param6 $1;

#P flonum 658 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 658 247 59 196617 param5 $1;

#P flonum 596 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 596 247 59 196617 param4 $1;

#P flonum 534 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 534 247 59 196617 param3 $1;

#P flonum 472 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 472 247 59 196617 param2 $1;

#P flonum 410 227 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 410 247 59 196617 param1 $1;

#P flonum 348 228 35 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 348 248 59 196617 param0 $1;

#P newex 416 304 76 196617 prepend param;

#P flonum 329 284 70 9 0 0 0 3 0 0 0 221 221 221 222 222 222 0 0 0;

#P message 329 303 78 196617 param delta $1;

#P message 223 303 82 196617 file vd.math.jxs;

#P newex 223 333 240 196617 jit.gl.shader test @file vd.math.jxs @name surface;

#P button 107 196 15 0;

#P newex 107 268 157 196617 jit.gl.mesh test @shader surface;

#P newex 107 218 142 196617 jit.matrix 3 float32 100 100;

#P newex 107 240 196 196617 jit.expr @expr "norm[0]" "norm[1]" "0";

#P message 158 81 34 196617 reset;

#P newex 158 101 186 196617 jit.gl.handle test @inherit_transform 1;

#P newex 23 101 48 196617 r render;

#P toggle 138 72 15 0;

#N vpatcher 53 128 279 297;

#P inlet 106 30 15 0;

#P window setfont "Sans Serif" 9.;

#P window linecount 0;

#P newex 43 95 47 196617 gate 1 1;

#P newex 42 116 41 196617 s draw;

#P window linecount 1;

#P newex 17 52 58 196617 t b b erase;

#P inlet 17 32 15 0;

#P outlet 17 83 15 0;

#P connect 1 0 2 0;

#P fasten 2 2 0 0 70 75 22 75;

#P connect 2 0 0 0;

#P connect 4 0 3 0;

#P fasten 5 0 4 0 111 88 48 88;

#P fasten 2 1 4 1 46 83 85 83;

#P lcolor 15;

#P pop;

#P newobj 76 101 42 196617 p Draw;

#P toggle 214 40 15 0;

#P message 214 60 68 196617 fullscreen $1;

#N vpatcher 30 89 166 253;

#P window setfont "Sans Serif" 9.;

#P newex 50 71 35 196617 sel 27;

#P newex 50 50 40 196617 key;

#P outlet 50 93 15 0;

#P connect 1 0 2 0;

#P connect 2 0 0 0;

#P pop;

#P newobj 231 40 33 196617 p Esc;

#P newex 214 78 151 196617 jit.window test @depthbuffer 1;

#P toggle 76 53 15 0;

#P newex 76 72 57 196617 qmetro 30;

#P newex 76 128 187 196617 jit.gl.render test @erase_color 0 0 0 1;

#P connect 33 0 20 0;

#P connect 31 0 20 0;

#P connect 29 0 20 0;

#P connect 27 0 20 0;

#P connect 25 0 20 0;

#P connect 23 0 20 0;

#P connect 21 0 20 0;

#P connect 34 0 33 0;

#P connect 32 0 31 0;

#P connect 30 0 29 0;

#P connect 28 0 27 0;

#P connect 26 0 25 0;

#P connect 24 0 23 0;

#P connect 22 0 21 0;

#P fasten 20 0 16 0 421 325 228 325;

#P connect 13 0 12 0;

#P connect 12 0 14 0;

#P connect 19 0 18 0;

#P connect 17 0 16 0;

#P fasten 18 0 16 0 334 321 228 321;

#P connect 5 0 3 0;

#P connect 6 0 5 0;

#P connect 4 0 6 0;

#P connect 11 0 10 0;

#P fasten 8 0 7 1 143 94 113 94;

#P connect 15 0 13 0;

#P fasten 10 0 0 0 163 123 81 123;

#P connect 7 0 0 0;

#P fasten 9 0 0 0 28 123 81 123;

#P connect 1 0 7 0;

#P connect 2 0 1 0;

#P window clipboard copycount 35;

////////////////////////////////////////////////////////////////Shader

varying vec3 N;

varying vec3 P;

#define PI 3.141519

#define TWOPI 6.28

uniform float delta;

uniform float param0;

uniform float param1;

uniform float param2;

uniform float param3;

uniform float param4;

uniform float param5;

uniform float param6;

vec3 surface(vec2 point)

{

vec3 s_point;

/*

s_point.x = 2.*sin(3.*point.x) / (2. + cos(point.y));

s_point.y = 2.*(sin(point.x) + 2.*sin(2.*point.x)) / (2. +

cos(point.y + 2.*PI / 3.));

s_point.z = (cos(point.x) – 2.*cos(2.*point.x))*(2. +

cos(point.y))*(2. + cos(point.y + 2.*PI / 3.)) / 4.;

*/

s_point.x = param1*(1.-((param0*param5*point.x*TWOPI)/TWOPI))*cos(param0*param4*param5*point.x*TWOPI)*(1.+cos(param0*param6*point.y*PI))+param3*cos(param0*param4*param5*point.x*TWOPI);

s_point.y = param1*(1.-((param0*param5*point.x*TWOPI)/TWOPI))*sin(param0*param4*param5*point.x*TWOPI)*(1.+cos(param0*param6*point.y*PI))+param3*sin(param0*param4*param5*point.x*TWOPI);

s_point.z = param2*((param0*param5*point.x*TWOPI)/TWOPI)+param1*(1.-((param0*param5*point.x*TWOPI)/TWOPI))*sin(param0*param6*point.y*PI);

return s_point;

}

void main (void)

{

vec4 V = vec4( surface(gl_Vertex.xy), 1.);

//get the vertices into eye space

P = (gl_ModelViewMatrix*V).xyz;

vec3 neighbour1 = surface(vec2(gl_Vertex.x + delta, gl_Vertex.y));

vec3 neighbour2 = surface(vec2(gl_Vertex.x, gl_Vertex.y + delta));

vec3 tangent = normalize(neighbour1 – V.xyz);

vec3 bitangent = normalize(neighbour2 – V.xyz);

//get the normals into eye space

N = normalize( cross(tangent, bitangent) );

N = mat3( gl_ModelViewMatrixInverseTranspose[0].xyz,

gl_ModelViewMatrixInverseTranspose[1].xyz,

gl_ModelViewMatrixInverseTranspose[2].xyz)*N;

gl_Position = gl_ModelViewProjectionMatrix*V;

}

]]>

uniform vec4 Ia;

uniform vec4 Ii;

uniform vec3 eyePosition;

uniform vec4 Ke;

uniform vec4 Ka;

uniform vec4 Kd;

uniform vec4 Ks;

uniform float Ns;

varying vec3 N;

varying vec3 P;

void main (void)

{

vec3 Nn = normalize(N);

//ambient contribution

vec4 ambient = Ia*Ka;

vec3 lightPosition = vec3(gl_LightSource[0].position);

//diffuse contribution

vec3 L = normalize(lightPosition – P);

vec4 diffuse = Kd*Ii*max(dot(Nn, L), 0.);

//calculate specular contribution

vec3 V = normalize(eyePosition – P);

vec3 H = normalize(L + V); //average of lighting and view vector)

not true reflection vector

vec4 specular = Ks*Ii * pow(max(dot(Nn,H), 0.), Ns);

gl_FragColor = Ke + ambient + diffuse + specular;

}

]]>

I couldn’t let Wes have all the fun, so here is a version that does

Spherical Harmonics. I also threw in my special normal-dependent

pixel-disposal sauce to make it that much more interesting. Try

animating the delta value and changing the fig/thresh params. Also, the

sphere param mixes between a sphere and the distorted shape, and of

course you can extrapolate. For those of you not running the latest and

greatest hardware, I recommend lowering the jit.matrix dims. It does

look really nice at 500×500 though

I’m not usually a big supporter of mathematical surfaces, but I guess

I’m beginning to see the light.

Happy Patching!

Andrew B.

oops, that patch had a minor issue. This one will work better.

AB

All it did on my computer was completely kill the framerate for a black screen.

Max 4.6.3 Jitter 1.6.3

1.67 Ghz Powerbook G4

OS 10.4.10

May just be my problem,

Keith

On 9/14/07, Andrew Benson

> oops, that patch had a minor issue. This one will work better.

>

> AB

>

>

>

>

Hi Kieth,

Try lowering the dimensions of the shape matrix to something reasonable

(say 50×50) or something and see if that helps. It could just be that

the shader pushes the limits of your graphics card. I’ll have a look at

it later and see if I can streamline the code a little.

AB

> Try lowering the dimensions of the shape matrix to something reasonable

> (say 50×50) or something and see if that helps.

That helped with the framerate. I was confused by the complete black

jit.window. A little jit.gl.handle and parameter tweaking did the

trick too.

Thanks,

Keith

Andrew Benson skrev:

> oops, that patch had a minor issue. This one will work better.

Looks fantastic!

I have just one issue with it, and that is that the texture params "fig"

and "thresh" control an effect that seems quite jagged – is there a way

to antialias there? That would make it even more awesome.

Andreas.

andrew.

can you show the web forum some love?

many thanks to all

Unfortunately, this effect makes use of the "discard" method, which is

either on or off. Basically, it’s just throwing out pixels if the

output of a formula falls below a threshold. If you don’t care about

depthbuffering, you can use a

smoothstep(thresh,thresh+fwidth(goop)*fade,goop); and apply that to the

glFragColor alpha.

If you do want to keep the depthbuffering and need antialiasing, you

could always render to a double-size texture and do averaging

downsamples, as has been discussed previously.

Best,

Andrew B.

Oh, sorry. Here you go.

AB

Here’s a random question about this… not to provoke an argument,

but simply because I’m always curious about the "best" way of doing

anything.

Why do all this in a shader, why not use jit.expr to calculate

geometry for surfaces and then the normals? Is there a benefit to

first calculating a normal-distribution-based coordinate spread for a

plane, and then using that as input into a shader?

I have a friend who does a lot of OpenGL programming, and he swears

by using only vertex arrays (especially cached) and standard opengl

blend modes. But he doesn’t know shaders well, I feel like there

could be something he’s missing.

Cheers

Evan

On Sep 14, 2007, at 2:39 AM, Wesley Smith wrote:

> Hi folks,

> Here’s a shader and patch I made following the "Calculate Normals In

> Shader" blog entry on http://tonfilm.blogspot.com/ . It should be

> quite easy to plug in other equations from

> http://local.wasp.uwa.edu.au/%7Epbourke/surfaces_curves/ .

>

> This one implements the Tranguloid Trefoil equation:

> http://www.mat.ucsb.edu/~whsmith/ShaderSurface.zip

>

>

> wes

On Sep 17, 2007, at 3:16 PM, evan.raskob [lists] wrote:

> Here’s a random question about this… not to provoke an argument,

> but simply because I’m always curious about the "best" way of doing

> anything.

I guess my general practice is to let the system breathe… So stuff

as much as you can on a GPU, until it strats to choke.

> Why do all this in a shader, why not use jit.expr to calculate

> geometry for surfaces and then the normals? Is there a benefit to

> first calculating a normal-distribution-based coordinate spread for

> a plane, and then using that as input into a shader?

It is simple, and since the vertices are sent only once – there’s

almost no performance hit.

Also, as far as I know, shaders are incapable of creating new vertices.

> I have a friend who does a lot of OpenGL programming, and he swears

> by using only vertex arrays (especially cached) and standard opengl

> blend modes. But he doesn’t know shaders well, I feel like there

> could be something he’s missing.

he’s missing a lot! Many of the patches posted lately on the list

would be very hard, if not impossible, to recreate while using only

GPU without the shaders.

just my 0.2RSD

nesa

> > Why do all this in a shader, why not use jit.expr to calculate

> > geometry for surfaces and then the normals? Is there a benefit to

> > first calculating a normal-distribution-based coordinate spread for

> > a plane, and then using that as input into a shader?

>

Imagine doing this:

x = 2 sin(3 u) / (2 + cos(v))

y = 2 (sin(u) + 2 sin(2 u)) / (2 + cos(v + 2 pi / 3))

z = (cos(u) – 2 cos(2 u)) (2 + cos(v)) (2 + cos(v + 2 pi / 3)) / 4

with jit.expr at 30fps on a 50×50 grid. You’re framerate will crawl

if not die if you do it on the CPU. On the GPU you can warp the

coordinate range and other fun stuff dynamically for smooth

animations. jit.gl.mesh does use VBOs under the hood for sending the

planar data so that is also fast.

> It is simple, and since the vertices are sent only once – there’s

> almost no performance hit.

> Also, as far as I know, shaders are incapable of creating new vertices.

>

Not entirely true. With Geometry shaders, you can generate quite a

large number of vertices on the GPU.

>

> > I have a friend who does a lot of OpenGL programming, and he swears

> > by using only vertex arrays (especially cached) and standard opengl

> > blend modes. But he doesn’t know shaders well, I feel like there

> > could be something he’s missing.

VBOs are definnitely best practice for dispatching data to the GPU but

shaders are required for complex rendering effects. Blend modes are

nice but limited. For example, you can’t do per-pixel lighting with

just VBOs and blend modes.

wes

> Not entirely true. With Geometry shaders, you can generate quite a

> large number of vertices on the GPU.

ah yes!(that is too new for my hardware:)

also, how would one do that with jitter?

> also, how would one do that with jitter?

Wait for a version of Jitter that supports it.

wes

Hiya peepz,

I’ve been following this thread for a while and it is really fun.

This is my first attempt at 3D open GL, so I am having quite a time

with it. I sat and watched Dan Vatsky at Share the other week and we

were wondering why it isn’t possible to morph a 3D shape he had

designed in maya in Jitter. And you guys point us in the some near

direction of that now!

Basically I wrote the shader to mix() between two of the shapes

documented on the website mentioned in the beginning of this thread.

Values for the param "sphere" – 0. shows the owl shape and 1. shows

the mobius strip. After some tuning of the incoming points each

formula seems to work and the fade does too. What I am having

difficulty with is the lighting and adding a video texture.

Am I right to assume that the lighting and texturing is done for each

point on the shape (and only one "side") and not as a shape in

general and that is why the lighting and texturing don’t quite work?

Surprisingly I could figure out the math, but I am fairly new to

this. Any explanation on what is happening with lighting and

texturing and how it might work would help.

Thanks again for all the sharing and the great ideas on the list.

(())_n

Nice. Here’s a question for you:

How do you imagine the video on the form looking? Is is mapped to the

surface directly? Is it reflection mapped? For the former, you will

have to generate texture coordinates in some fashion. The simplest

way it linearly with the input points. For the latter, just take the

fragment shader portion of the refract.reflect shader I posted last

week and it should work.

wes