Shader as texture for primitive

Is it possible to use a shader as a texture on a 3d primitive? When I invoke shader the effect is to render the sphere as a flat 2d object with the shader as a fill rather than a texture.

I thought I could call texture(myshader) but it appears to demand a PImage.

1 Like

see listing 2.1

in https://processing.org/tutorials/pshader/

1 Like

Thanks! That makes a lot of sense.

If your shader looks like a 2D fill it’s because when the shader calculates the color of a pixel it only takes into account the screen 2D position of each vertex.

If the shader calculations use the x, y and z components of the vertex to calculate the color, then it will no longer be a flat sticker.

Here a simple example.

A question is then how to use the 3D vertices to calculate a texture in the shader?

ps. To use model space coordinates you will need to call hint(DISABLE_OPTIMIZED_STROKE) in your program, otherwise the position attribute is the shader is no longer in model space, and the shader generated texture would not rotate when rotating the object in 3D.

But maybe you wanted a simple example instead of all these details? :slight_smile:

import peasy.PeasyCam;
PeasyCam cam;
PShader fx;

void setup() {
  size(600, 600, P3D);
  cam = new PeasyCam(this, 400);
  fx = loadShader("fx.frag", "fx.vert");
  shader(fx);  
  noStroke();
  hint(DISABLE_OPTIMIZED_STROKE);
}

void draw() {
  background(0);
  sphere(150);
}
// fx.vert
uniform mat4 transformMatrix;
attribute vec4 position;

varying vec3 p;

void main() {
  gl_Position = transformMatrix * position;
    
  p = position.xyz;
}
// fx.frag
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec3 p;

void main() {
  float gray = step(0.5, fract(p.x * 0.05));
  gl_FragColor = vec4(vec3(gray), 1.0);
}

Try remove the hint(), run the program and rotate the sphere.

1 Like

Haha… I’d be lying if I didn’t want a simpler way but you know it’s all learning and I’m grateful for the full answers. I kind of expected the 2d shader to be treated like a PImage and just ‘wrap’ the 3d primitive like texture() seems to work.

So as I understand it the .vert file takes the (let’s say 2d shader) and transforms it for 3d space?

The vert and frag shaders work together. vert runs first, then frag. If you only use the fragment shader, Processing provides a default vertex shader for you. The vertex shaders main goal is to decide where in the screen each vertex should be drawn, depending on translations, rotations and the 3D shape you are using. Then, once the screen location is known, the fragment shader decides the color for all the pixels that form a triangle (made out of 3 vertices). More or less :slight_smile:

You can probably do what you are asking: use a PGraphics with a shader and render the texture there, then call texture(…) with your PGraphics (containing the shader texture) as an argument. Basically it’s splitting the problem in two steps: since you can not call texture() with a shader, render the shader to an image first, then call texture().

3 Likes

What you’re describing in the second paragraph does seem more straight forward. But I am but a shader padawan. Thanks!

1 Like

Is there a resource for free shaders?

There’s many places where one can find shaders online. 4 examples:

but they must be made compatible with Processing. For Shadertoy there’s an library. The shader language is usually the same (GLSL) but the names of the uniforms and the attributes used in each platform vary, and the architectures also change a bit.

Most shaders you find online tend to be fragment shaders. There’s not many examples of vertex shaders.

Afaik there’s no database of free shaders for Processing, but we could make one :slight_smile:

2 Likes

Sounds very interesting. What did you have in mind – like an organized list of links, or like a repo that compiles many example files from different sources, or something else?

Many of the GLSL Sandbox shaders are compatible. although it’s a very hit and miss collection! :smile:

There are a couple of PraxisLIVE nodes supporting live shader editing that might be useful even if you want to use them in a vanilla Processing project later.

Actually I could imagine this for game sprites / sounds, svgs… as well…

I thought of providing a bunch of 2D and 3D shader effects to use in projects. When I have time I can start by populating the list on the right side of the Processing Shader Tool

I thought it would be cool to show examples of using shaders as materials, even if people can’t write those shaders, they can activate them to render 2D and 3D shapes with strange (non flat) colors.

1 Like

Using a shader as a texture

PShader fx;
PShape cube;
PGraphics tex;

void setup() {
  size(640, 360, P3D);
  tex = createGraphics(200, 200, P3D);
  
  fx = loadShader("frag.glsl");
  tex.beginDraw();
  tex.shader(fx);
  tex.noStroke();
  tex.rect(0, 0, tex.width, tex.height);
  tex.endDraw();
  
  cube = makeCube();
  cube.setTexture(tex);
  fill(255);
  noStroke();
}

// We need a 3D model, ideally with UV coordinates for the shader texture.
// In this case we create a 3D cube, but we could replace this function just by
// loadShape() to load a 3D model from disk.
PShape makeCube() {
  PShape s = createShape();
  s.beginShape(QUADS);

  // +Z "front" face
  s.vertex(-1, -1, 1, 0, 0);
  s.vertex( 1, -1, 1, 1, 0);
  s.vertex( 1, 1, 1, 1, 1);
  s.vertex(-1, 1, 1, 0, 1);

  // -Z "back" face
  s.vertex( 1, -1, -1, 0, 0);
  s.vertex(-1, -1, -1, 1, 0);
  s.vertex(-1, 1, -1, 1, 1);
  s.vertex( 1, 1, -1, 0, 1);

  // +Y "bottom" face
  s.vertex(-1, 1, 1, 0, 0);
  s.vertex( 1, 1, 1, 1, 0);
  s.vertex( 1, 1, -1, 1, 1);
  s.vertex(-1, 1, -1, 0, 1);

  // -Y "top" face
  s.vertex(-1, -1, -1, 0, 0);
  s.vertex( 1, -1, -1, 1, 0);
  s.vertex( 1, -1, 1, 1, 1);
  s.vertex(-1, -1, 1, 0, 1);

  // +X "right" face
  s.vertex( 1, -1, 1, 0, 0);
  s.vertex( 1, -1, -1, 1, 0);
  s.vertex( 1, 1, -1, 1, 1);
  s.vertex( 1, 1, 1, 0, 1);

  // -X "left" face
  s.vertex(-1, -1, -1, 0, 0);
  s.vertex(-1, -1, 1, 1, 0);
  s.vertex(-1, 1, 1, 1, 1);
  s.vertex(-1, 1, -1, 0, 1);

  s.endShape();

  s.setStroke(false);
  s.setTextureMode(NORMAL);

  return s;
}

void draw() {
  // the shader has a uniform called time
  // to make change on every frame, otherwise we might
  // as well just use an image loaded from disk instead of a shader :)
  fx.set("time", millis()*0.01);
  tex.beginDraw();
  tex.rect(0, 0, tex.width, tex.height);
  tex.endDraw();
  // notice that for the tex pgraphics we just draw a rectangle
  // covering it. The colors of that rectangle come from the shader.

  background(255);
  translate(width/2, height/2, -100);
  rotateX(millis() * 0.001);
  rotateY(millis() * 0.0005);
  scale(90);
  shape(cube);
}

A simple shader doing some rgb wavy nonsense

// frag.glsl
#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

uniform float time;

void main() {
  vec2 p = (gl_FragCoord.xy - 100.0) * 0.03;
  float r = 0.5 + 0.5 * sin(p.x * p.y * 1.1 + time);
  float g = 0.5 + 0.5 * sin(p.x * p.y * 1.2 + time);
  float b = 0.5 + 0.5 * sin(p.x * p.y * 1.3 + time);
  gl_FragColor = vec4(r, g, b, 1.0);
}

cube

2 Likes