A couple of nice shaders

One is an embossing shader, but the other is really nice. It creates a fake spherical effect, wrapping a square texture around the “sphere”. This video demonstrates. There is no sphere, only a mesh with a rect and a square texture.

http://www.youtube.com/watch?v=HxZgP_S0IIA

Code for both is here: https://gist.github.com/dermotbalson/6886024

i love shaders, nice work @ignatz

As always @Ignatz, amazing!

This is great! Thanks!
Could you tweek it to add a vec3 for the lightening source and add a shadow?

Very nice! Here’s a small variant:

https://www.youtube.com/watch?v=UAlt16TN7uc

I find that when I look at the very edge of the sphere then I’m not sure that what I see is what I expect to see. But I need to look at the mathematics of the code to understand whether that’s real or not.

You say in the code that “fraction” changes the behaviour. I don’t see that. It looks to me like an initial offset.

The mod function is overloaded and works fine on vec2s. So you can condense a few lines in the shader:

vec2 uv = mod((fraction + time)*vec2(1.,1.) + p*f,vec2(1.,1.));
vec4 c = texture2D( texture, uv);

(You could condense it even further if you wanted to.)

I realise I implicitly took credit for that code. Actually I found both of them on the net, and tweaked them a little.

Thanks, @Andrew_Stacey, for your suggestion - actually, in the original code, here

http://adrianboeing.blogspot.com/2011/02/sphere-effect-in-webgl.html

there was no fraction factor (ie effectively it was 1), and the image was tiling itself several times. I found that by introducing this factor, I was able to get what looks like a single instance of the image to wrap around.

So it was all guesswork…

@Jmv38 - lol, no chance of adding those things! Not until I understand the code, anyway…

@Ignatz I had a look at that link. I can see, sort of, what was irritating me about the spherical one: it approximates the sphere by a parabaloid and the approximation is bad at the edge of the sphere.

I don’t think it’s that difficult to do correctly so here’s the fragment shader for a true sphere. You need to set the height parameter of the shader; it’s the height of the eye above the sphere. I used 15.

precision highp float;
 
uniform lowp sampler2D texture;
uniform float time;
uniform float fraction;
uniform float height;
highp float h = height*height;
highp float rh = h/(h-1.);
highp float sqrh = sqrt(rh);
 
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec4 vPosition;

void main()
{
  vec2 p = (-1.0 + 2.0 * vTexCoord)/sqrh;
  float r = dot(p,p);
  if (r > 1.) discard; 
  float t = (h + sqrt(r - r*h + h))/(r+h);
    float phi = 1.-acos(t*p.y)/3.1415;
    float theta = mod(atan(
        (1.-t)*height,
        t*p.x)/(2.*3.1415)+time,1.);
  vec4 c = texture2D( texture, vec2(theta,phi));
  gl_FragColor = vec4(c.xyz, 1.0);
}

@Andrew_Stacey - thank you, as always!

Now with added lightness (@Jmv38, this one’s for you).

function setup()
    m=mesh()
    img0=readImage("Cargo Bot:Codea Icon") --select your image here <<<<<
    mm=math.min(img0.width,img0.height) 
    img=img0:copy(1,1,mm,mm)            --make it square
    m:addRect(WIDTH/2,HEIGHT/2,400,400)   --add a rectangle to our mesh, the size you want (square)
    m.texture=img
    m.shader=shader(
    sphereShader.vertexShader,sphereShader.fragmentShader)
    m.shader.height=15
    m.shader.light = vec3(1,1,0):normalize()
    m:setColors(color(255))
    light_azimuth = 0
    light_zenith = 0
    local reset_light = function()
        m.shader.light = vec3(
            math.cos(math.rad(light_azimuth)) 
            * math.sin(math.rad(light_zenith)),
            math.cos(math.rad(light_zenith)),
            math.sin(math.rad(light_azimuth)) 
            * math.sin(math.rad(light_zenith))
            )
        end
    parameter.integer("light_azimuth",-180,180,0,reset_light)
    parameter.integer("light_zenith",0,180,0,reset_light)
end
 
function draw()
    background(40, 40, 50)
    m.shader.time=ElapsedTime/10 --vary the time speed to change the rotation speed of the sphere
    m:draw()
end
 
sphereShader = {
vertexShader = [[
 
uniform mat4 modelViewProjection;
 
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
 
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
 
void main()
{
    vColor=color;
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}
 
]],
fragmentShader = [[
 
precision highp float;
 
uniform lowp sampler2D texture;
uniform float time;
uniform float height;
uniform vec3 light;

highp float h = height*height;
highp float rh = h/(h-1.);
highp float sqrh = sqrt(rh);


varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec4 vPosition;

void main()
{
  vec2 p = (-1.0 + 2.0 * vTexCoord)/sqrh;
  float r = dot(p,p);
  if (r > 1.) discard; 
  float t = (h + sqrt(r - r*h + h))/(r+h);
    float l = max(0.,t*dot(p.xy,light.xy) + (1.-t)*height*light.z);
    float phi = 1.-acos(t*p.y)/3.1415;
    float theta = mod(atan(
        (1.-t)*height,
        t*p.x)/(2.*3.1415)+time,1.);
  vec4 c = texture2D( texture, vec2(theta,phi));
  gl_FragColor = vec4(l*c.xyz, 1.0);
}
 
 
]]}

Nice work! It needs a good image to show it off properly

http://www.youtube.com/watch?v=If5fjdO0z0Q

Who said we would never see the dark side of the moon! These shaders are great!

Thank you very much @andrew_stacey, this is great. However i get this on my ipad1:
y
Any idea of what is going on? The oval shape? The column in the middle? How can i correct this? With ignatz first code the sphere was correct…

Obviously, your iPad is not perfectly flat. Have you dropped it? :wink:

Obviously, your iPad is not perfectly flat. Have you dropped it? :wink:

@Ignatz Haha… Double post.

@Jmv38 Maybe you’re in portrait mode and need to be in landscape mode?

No, I think his iPad is in ellipse mode…

Lol!
Seriously, any idea? I am in lanscape, and i just copied/pasted the code, no change.

@Dave1707 you have the same ipad as me: could you try andrew’s program to check if you have the same pb as me? Thanks in advance!

@Jmv38 - yep, I tried it on an iPad1 and got the same result you did

I think it will take a mathematician to explain why!

Thanks for checking. Meanwhile i did my homework. Here are the result:
http://www.youtube.com/watch?v=gYWcfz42B-w
You can see in this video i have made one shader with the 2 options. I am very unfamiliar with shaders, so i have not been able to switch from onse shader to the other from a running program. Actually, i have, but the result is crazy, so i quit. So, to go from one shader to the other you must comment one of the 2 lines as shown in the video. I really dont understand what is going on. Can anyone help please?
I have the feeling the root cause is around how the shaders are implemented in ios5.1 or ipad1 or codea support of these, or a tricky combination of the three…
Here is the code:


--# SphereShader
sphereShader = {
vertexShader = [[

uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    vColor=color;
    vTexCoord = texCoord;
    gl_Position = modelViewProjection * position;
}

]],

fragmentShader = [[
 
precision highp float;

uniform float model;

uniform lowp sampler2D texture;
uniform float time;
uniform float fraction;
uniform float height;
uniform vec3 light;

highp float h = height*height;
highp float rh = h/(h-1.);
highp float sqrh = sqrt(rh);

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying vec4 vPosition;
 

void main()
{

  vec2 p = (-1.0 + 2.0 * vTexCoord)/sqrh;
  float r = dot(p,p);
  if (r > 1.0) discard; 

  vec2 uv;
  vec2 uv1;
  vec2 uv2;

  // texture coordinates according to Ignatz
  float f = 0.5*(1.0-sqrt(1.0-r))/r;
  uv1.x = mod( fraction+p.x*f + time , 1.0 );
  uv1.y = mod( fraction+p.y*f + time , 1.0 );

  // texture coordinates according to Andrew
  float t = (h + sqrt(r - r*h + h))/(r+h);
  float phi = 1.-acos(t*p.y)/3.1415;
  float theta = atan((1.-t)*height,t*p.x) / (2.*3.1415) + time ;
  uv2.x = mod( theta , 1.0 );
  uv2.y = mod( phi , 1.0 );

  // choose uv according to model
// attempt to do it from parameter, but the result is crazy and i dont understand why.
//  if (model<0.5)  uv = uv1;  
//  if (model>0.5)  uv = uv2;
  // choose manually your model by commenting one of the lines:
  //uv = uv1;
  uv = uv2;

  // set the texture coordinates
  vec4 c  = texture2D( texture, uv );

  // shadow application
  float l = max(0.,t*dot(p.xy,light.xy) + (1.-t)*height*light.z);

  gl_FragColor = vec4(l*c.xyz, 1.0);


}
 
]]}






--# Main
-- sphere shader 2

-- by andrew stacey
function setup()
    m=mesh()
    img0=readImage("Cargo Bot:Codea Icon") --select your image here <<<<<
    mm=math.min(img0.width,img0.height) 
    img=img0:copy(1,1,mm,mm)            --make it square
    m:addRect(WIDTH/2,HEIGHT/2,400,400)   --add a rectangle to our mesh, the size you want (square)
    m.texture=img
    m.shader=shader(
    sphereShader.vertexShader,sphereShader.fragmentShader)
    m.shader.height=15
    m.shader.light = vec3(1,1,0):normalize()
    m:setColors(color(255))
    light_azimuth = 0
    light_zenith = 0
    local reset_light = function()
        m.shader.light = vec3(
            math.cos(math.rad(light_azimuth)) 
            * math.sin(math.rad(light_zenith)),
            math.cos(math.rad(light_zenith)),
            math.sin(math.rad(light_azimuth)) 
            * math.sin(math.rad(light_zenith))
            )
        end
    model = 0.0
    m.shader.model = model
    local setModel = function() m.shader.model = model*1.0 end
    parameter.integer("model",0,1,0,setModel)
    parameter.integer("light_azimuth",-180,180,-34,reset_light)
    parameter.integer("light_zenith",0,180,90,reset_light)
end

function draw()
    background(40, 40, 50)
    m.shader.time=ElapsedTime/10 --vary the time speed to change the rotation speed of the sphere
    m:draw()
end