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

Add an example for resetShader() #5265

Merged
merged 4 commits into from
Dec 23, 2021

Conversation

JetStarBlues
Copy link
Contributor

Changes:
Adds an example for resetShader(). Here is a link to a live demo.

Screenshot of the change:
resestShaderEx

@aferriss
Copy link
Contributor

Looks good to me, thanks for making the example @JetStarBlues!

@aferriss
Copy link
Contributor

Actually, one small nit. I wonder if it would be better to inline the shaders (rather than loading from files), so that the user can see the custom shader more clearly.

@JetStarBlues
Copy link
Contributor Author

JetStarBlues commented May 30, 2021

I wonder if it would be better to inline the shaders (rather than loading from files), so that the user can see the custom shader more clearly.

I've also been thinking quite a bit about this. There's a similar issue with all* the shader examples - both in the Reference and Examples sections of the website. It's a bit of an ask to require users to dig through the p5 source code to find the referenced shader files.

I'm not sure what code is used to display the "live code" view above (with the edit/reset/copy buttons). However, it seems to work with the assumption that all the code needed to run the program resides in a single "sketch.js" file. The shader files seem to be treated as an unchangeable asset like images and sound.

If there was a way to somehow make the live-code-viewer (not sure correct name) show the shader files, I think that would be a nice solution, as you would:

  • get syntax highlighting
  • avoid cluttering the "sketch.js" code (esp with large shader files)
  • avoid the "wall of text" effect (esp if first exposure (new learner) to shaders and glsl syntax - where you would be presented with a wall of un-highlighted text using cryptic syntax)
  • have the ability to use the same file in multiple sketches (for example the vertex shader which doesn't change between programs)

If this isn't an easy thing to do, (or not in the spirit of the live-code-viewer design), (or in the meanwhile), I can go ahead and change this example to use inline shader code. Should this inline change be propogated to all examples (excluding loadShader())?

* except createShader()

@JetStarBlues
Copy link
Contributor Author

JetStarBlues commented May 30, 2021

Here's a live version of the code as inline.

@JetStarBlues
Copy link
Contributor Author

If the comments are removed from the inline shader code, the code becomes more succinct, and looks much nicer (live version).

In this case, the assumption becomes that the user is comfortable with GLSL, or that they will go to another resource to learn about it (and the uniforms/attributes provided by p5).

@JetStarBlues
Copy link
Contributor Author

@limzykenneth I was looking through the source code, and saw that you did most of the work on the code renderer. What do you think? Is the renderer capable of multi-file viewing/editing?

@limzykenneth
Copy link
Member

Ah, this file. 😝 I didn't actually write it, just refactored it and fixed a few bugs when I was working on the reference. In essence I know almost as much as you do from just reading the code.

Looking at the idea you are discussing above I'm guessing something like including the shader code in the documentation code block and then importing it as string to be compiled when the example sketch is running would be something that you are thinking of? (Not sure if this description is clear enough)

We can do something similar with the CSS norender and notest class and mark a certain block as shader with the shader code in it and in the code renderer retrieve its content, and since the code renderer is already using eval just eval that shader code into a variable available to the sketch.

Not sure how easy or possible with the current example renderer but I had wanted to rewrite it for a long time now, just didn't have the time to do it as it's going to be a relatively big project.

src/webgl/material.js Outdated Show resolved Hide resolved
@JetStarBlues
Copy link
Contributor Author

@limzykenneth

Thank you for the detailed reply and insight into how the code works!

something like including the shader code in the documentation code block and then importing it as string to be compiled when the example sketch is running

I see, so in this scenario, the shader files would not reside in the assets/ directory. Instead they would each reside in their own code block within the documentation.

<!-- shader.vert -->
<div class="shader" id="shader.vert">
<code></code>
</div>

<!-- shader.frag -->
<div class="shader" id="shader.frag">
<code></code>
</div>

<!-- sketch.js -->
<code></code>

The class shader would allow for glsl syntax highlighting. And potentially, the ability to toggle code visibility (ex. via click-to-reveal/hide).

I'm not sure how this would work without intercepting calls to loadShader() (or createShader(). Would local storage be involved?

---

A thought. If,

  1. code folding is supported by the renderer
    • (and the ability to have an initial fold state)
  2. a custom prism.js rule is added that applies glsl syntax-highlighting to stringified glsl code
    • for example, it could follow the convention of this extension, where /*glsl*/ is used as a marker to indicate that the contents of the subsequent string should be highlighted as glsl:
let vertSrc = /*glsl*/ `

    // vertex position
    attribute vec3 aPosition;

    // main
    void main () {
        gl_Position = vec4(aPosition, 1.0);
    }
`;

Then it might be possible to get some of the benefits of both worlds. The examples can all be updated to use createShader and string variables. The variables containing the shader source can start out "folded" so as not to clutter the example. And the marker can be used to trigger glsl syntax highlighting.

@JetStarBlues
Copy link
Contributor Author

I can start a new issue for this (editor handling of shader files), and copy over some of the discussion here onto it.

@JetStarBlues
Copy link
Contributor Author

JetStarBlues commented Jun 8, 2021

Updated the code to use createShader and removed the {}. Comments omitted from shader source to minimize space.

resetShaderExample3

@limzykenneth
Copy link
Member

I'm not sure how this would work without intercepting calls to loadShader() (or createShader(). Would local storage be involved?

createShader() would probably be more appropriate in this case, especially if you want the user to be able to edit the shader.

code folding is supported by the renderer

That I'm not sure, I don't think it does but you can look it up.

a custom prism.js rule is added that applies glsl syntax-highlighting to stringified glsl code

prism.js supports GLSL syntax highlighting, not to inline strings but it can highlight it if it is in its own code block.

I see, so in this scenario, the shader files would not reside in the assets/ directory. Instead they would each reside in their own code block within the documentation.

If we go for this it means there's no need for inline GLSL syntax highlighting.

@JetStarBlues
Copy link
Contributor Author

I guess what I'm wondering is how the functions, lets say createShader would have access to the content in the shader code block.

Given that createShader expects string pointers as arguments, how would this work? Would this require intercepting the call to createShader(vertSrc, fragSrc) and somehow feeding it contents of the shader code blocks? How would the code in sketch.js look - would there be a need to declare the vertSrc and fragSrc variables? I.e. can it work with the vanilla createShader API? Or would a variant be necessary.

That I'm not sure, I don't think it does but you can look it up.

Unfortunately it doesn't. It seems to be a bare bones editor/renderer.

@limzykenneth
Copy link
Member

I don't think intercepting calls to createShader will be necessary. The shader code itself can be retrieved relatively easily by just querying the HTML content of the code block (cleaning up as necessary), not through the example code but rather in the renderer itself. Then the shader code can be saved to a string variable that is eval-ed along with the example code (vertSrc and fragSrc for example). Then the example code can directly call createShader(vertSrc, fragSrc) without modifications or manually defining vertSrc and fragSrc as if it is already defined.

I'm guessing the above works but I'm not sure what you mean by string pointers as Javascript strings are immutable so they effectively can't be passed by reference.

@JetStarBlues
Copy link
Contributor Author

JetStarBlues commented Jun 8, 2021

Thank you for taking the time to reply!

Sorry, it's taking me a couple of tries to form a mental model.

Using the example above, would the documentation look like this?

<!-- vertSrc -->
<div class="shader">
<code id="vertSrc">
attribute vec3 aPosition;
attribute vec2 aTexCoord;
uniform mat4 uProjectionMatrix;
uniform mat4 uModelViewMatrix;
varying vec2 vTexCoord;
void main() {
  vTexCoord = aTexCoord;
  vec4 position = vec4(aPosition, 1.0);
  gl_Position = uProjectionMatrix * uModelViewMatrix * position;
}
</code>
</div>

<!-- fragSrc -->
<div class="shader">
<code id="fragSrc">
precision mediump float;
varying vec2 vTexCoord;
void main() {
  vec2 uv = vTexCoord;
  vec3 color = vec3(uv.x, uv.y, min(uv.x + uv.y, 1.0));
  gl_FragColor = vec4(color, 1.0);
}
</code>
</div>

<!-- sketch.js -->
<code>
function setup() {
  // Shaders require WEBGL mode to work
  createCanvas(100, 100, WEBGL);
  // Create our shader
  shaderProgram = createShader(vertSrc, fragSrc);
}

function draw() {
  // Clear the scene
  background(200);
  // Draw a box using our shader
    // shader() sets the active shader with our shader
    shader(shaderProgram);
    push();
    translate(-width / 4, 0, 0);
    rotateX(millis() * 0.00025);
    rotateY(millis() * 0.0005);
    box(width / 4);
    pop();
  // Draw a box using the default fill shader
    // resetShader() restores the default fill shader
    resetShader();
    fill(255, 0, 0);
    push();
    translate(width / 4, 0, 0);
    rotateX(millis() * 0.00025);
    rotateY(millis() * 0.0005);
    box(width / 4);
    pop();
}
</code>

Where, even though vertSrc and fragSrc are mentioned in sketch.js, they are not defined there. And instead their definition is implicit via the shader code blocks?

@aferriss
Copy link
Contributor

aferriss commented Jun 8, 2021

@JetStarBlues I could be wrong but I think you would need to add the ID to the code element itself and then query it like so

<code id="fragSrc">
   void main(){ gl_FragColor = vec4(1.0); }
</code>
const fragSrc = document.getElementById("fragSrc").innerHTML();

Also, do you not need to wrap the entire code element inside of a <pre> element to keep the formatting?

@JetStarBlues
Copy link
Contributor Author

would need to add the ID to the code element itself

Ah I see, thank you. I will update the ids above accordingly.

const fragSrc = document.getElementById("fragSrc").innerHTML();

So behind the scenes, this line will be injected into the code passed to eval? I.e. this line is prepended to the contents of sketch.js just before execution?

wrap the entire code element inside of a <pre> element

It looks like the renderer does this.

@limzykenneth
Copy link
Member

So behind the scenes, this line will be injected into the code passed to eval? I.e. this line is prepended to the contents of sketch.js just before execution?

Something along these lines yes. Though I am having second thoughts about this idea as it breaks the flow of the user just copying the code and be able to run it directly.

@JetStarBlues
Copy link
Contributor Author

@stalgiag I think this PR can be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants