Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader Texture Doesn't Work For Custom Bezier Shapes #5699

Open
1 of 17 tasks
michaelnorman-au opened this issue Jun 26, 2022 · 5 comments
Open
1 of 17 tasks

Shader Texture Doesn't Work For Custom Bezier Shapes #5699

michaelnorman-au opened this issue Jun 26, 2022 · 5 comments

Comments

@michaelnorman-au
Copy link

Most appropriate sub-area of p5.js?

  • Accessibility (Web Accessibility)
  • Build tools and processes
  • Color
  • Core/Environment/Rendering
  • Data
  • DOM
  • Events
  • Friendly error system
  • Image
  • IO (Input/Output)
  • Localization
  • Math
  • Unit Testing
  • Typography
  • Utilities
  • WebGL
  • Other (specify if possible)

p5.js version

v1.2.8 (vscode extension)

Web browser and version

102.0.5005.115 (Official Build) (64-bit) (cohort: Stable)

Operating System

Windows 10 Version 21H2 (Build 19044.1766)

Steps to reproduce this

https://editor.p5js.org/hellostorman/sketches/RjnrMmxG2

@welcome
Copy link

welcome bot commented Jun 26, 2022

Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, be sure to follow the issue template if you haven't already.

@davepagurek
Copy link
Contributor

Hi! I think this might be an issue with the shader and not an issue with Bezier shapes. If you replace your Vertex shader with the following code, your example works if you apply the shader directly to the shape:

attribute vec3 aPosition;

uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;

void main() {

  vec4 positionVec4 = vec4(aPosition, 1.0);
  gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
}

The gl_Position output uses units where the screen in the range [-1, 1]. P5's uProjectionMatrix is intended to map world-space coordinates into that range. (Applying P5's uModelViewMatrix before that takes transformations and camera positioning into account first.)

In your current vertex shader, it's taking the raw vertex position, multiplying it by 2, and subtracting 1. I believe the tutorial it came from makes the assumption that you'll be drawing a rectangle going from (0, 0) to (1, 1), in which case the shader maps it to the full screen. When you drew a rectangle to a graphic, your rectangle covered that range (and more!) so you ended up filling the graphic's canvas. Then, the default texture shader already includes the above code snippet, so everything displays. But when your shader is applied directly to the Bezier shape, the vertex shader ends up positioning the vertices offscreen, so it looks like nothing appears.

Let me know if I can help explain any of that better!

@michaelnorman-au
Copy link
Author

Hi Dave, cheers for your thorough response! I'm a bit new to all this, especially GLSL. I tried replacing the vertex shader with your snippet, and got no errors but also got a blank sketch. I wasn't really sure what you meant by apply the shader directly to the shape, is that different from the texture(shaderTexture) I did before the beginShape()?

image

Hopefully this illustrates the effect I'm trying to achieve. Say the shader was a radial gradient, I'm trying to map the coordinates of the shader into the warped bezier shape. I thought of a super hacky work around using an array of bezierPoint to build the shape, with the UV coordinates of the fragment shader being assigned in another array, but it sounds a bit expensive and I feel the vertex shader part you were talking about holds the answer, just a lack of comprehension on my part unforunately :)
Can I use the vertex shader to map the UV coordinates like I've shown above? And thanks again!

@davepagurek
Copy link
Contributor

davepagurek commented Jul 13, 2022

I wasn't really sure what you meant by apply the shader directly to the shape

Sorry, I wasn't super clear on that part! Instead of using texture and a graphics object at all, you can replace your draw function with this:

function draw() {
  background(255);
  shader(theShader);
 
  theShader.setUniform('resolution', [width, height]);
  theShader.setUniform('mouse', map(mouseX, 0, width, 0, 7));
  theShader.setUniform('time', frameCount * 0.01);

  beginShape();
  vertex(-25, 30, 20, 1, 0);
  bezierVertex(25, 30, 20, 25, -30, 20, -25, -30, 20);
  endShape();
}

I thought this was what you were originally trying to do, since this on its own without the vertex shader changes produces a blank canvas, but with the vertex shader I mentioned, looks like this for me:
image

But like you said, it doesn't bend the texture, which is a more general problem than just shader texures, since it applies to all textures.

Can I use the vertex shader to map the UV coordinates like I've shown above?

This is a thing that p5 can't do just yet! There are two things preventing it right now:

Once both of the above are fixed, you could specify different texture coordinates for your Bezier shape, which affect how the image in a texture() gets mapped to the shape you draw. There's already an issue open for the second one, maybe we could open another issue about the first for clarity?

As workarounds for now you could build your shape out of normal vertex calls instead of curves (basically doing some math yourself to make curves). You could either add texture coordinates to all vertices and then use texture() like your sketch currently does, or potentially use per-vertex fills, which will interpolate along each triangle. Here's an example using TRIANGLE_FAN to draw an oval with the center vertex being a different color from the rest:

function setup() {
  createCanvas(400, 400, WEBGL);
}

function draw() {
  background(255);
  noStroke();
  beginShape(TRIANGLE_FAN);
  
  // Center
  fill(255);
  vertex(0, 0);
  
  // Outline
  fill(0);
  for (let i = 0; i <= 20; i++) {
    const angle = (i / 20) * TWO_PI;
    vertex(150 * cos(angle), 100 * sin(angle));
  }
  
  endShape()
}

@michaelnorman-au
Copy link
Author

Instead of using texture and a graphics object at all, you can replace your draw function with this:

Ahh okay I understand now! For my specific use case I don't think I'll be able to use this method but nice to know for the future.

You can't pass texture coordinates to bezierVertex, quadraticVertex, or curveVertex (if you could, you could use those to specify how to map your gradient to the shape.)

That's exactly what I was looking for, I'll open up an issue after this. I think the normal vertex call workaround is the way to go for me right now.

Anyway, super grateful that you took the time for this. Have a good one!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Implement 2D feature to WebGL
Development

No branches or pull requests

3 participants