Jump to content
  • Advertisement
Sign in to follow this  
  • entry
  • comments
  • views

My new love : ShaderToy



(since shadertoy doesn't allow more than a couple of lines in the description and I don't want to put all the stuff into the comment section there, I abuse the blog here)

For everybody new - or not so new - to shaders, hear this: 

Browse and use ShaderToy

Not only is it just bloody amazing what you find there, it's veeeeeery educational. 
And easy to use. No hassle with setting up the GPU pipeline yourself - you only need a WebGL capable browser (most are). The interactive compiler displays errors right after the faulty code lines.
Minor nuisance for me was the sometimes "restrictive" behaviour of GLSL (coming from HLSL), but that's not deal breaking.

After experimenting with signed distance functions in 3D with my SlimDX/HLSL stuff I was curious about 2D. So I found this (thank you Marteen):



Very nice. Basic shapes with contours and the usual combiners. Improved with lighting and shadows. Expanding on this, experimenting with polygons and stars, I wanted to put it to the test with something less abstract. Anybody remember Spirograph ? The math around this (pun intended) is already endless. I didn't even know about roulettes before.

But let's start simple. There's a very old discovery from a Persian astronomer: Tusi-Couple.

Here a short protocol of the progress:

  • Needed individual colouring for the shapes (Marteen's sample combines all shapes to one SDF). Experimenting with arbitrary blending, too (blend function).

  • Animate the inner circle/wheel within the the outer. Just basic trigonometry. To make it more clear I colored both circles "Wheel of Fortune"-style (see radial function).

  • Choose some point on the inner wheel, mark it with a point. I chose a star shape for this.
  • Track the ellipse path. Since I got no clue how to derive that yet, I simply trial-and-errored.
  • Only got a filled ellipse (or rather: used a transformed circle). Found a real ellipse and used that to generate an outline (ellipse and ellipseLine functions)
  • The ellipse line produced some artifacts at the main axis. Needed some tweaking. Still not perfect, goes havoc in the degenerate case. Now use lineDist in that case.
  • Hmmmm, cogs would be nice. Splitting the polygon function into circleMod (to get normalized angle part) and using polyShape for laying cyclic functions around a circle. The current implementation using a simple sinus is actually NOT a clean SDF. It works well enough though - and I expect a correct implementation to be quite challenging.
  • Added spokes and bars to give even more of a mechanical/gear feeling. Steampunk rules.
  • Challenge: Derive ellipse path automatically. I feared the worst. Ellipses are usually a bitch - algebraically. But in this case I found that one can exploit the simplicity of the Tusi-couple and derive the major/minor axis directly (see final if-clause within sceneDist function. Not yet commented)
  • Add some light and make the thing scale with the viewport correctly (shadertoy has fullscreen capability).
  • TODO: Choose 2nd point with mouse. This should be possible with additional input/buffer logic of the shadertoy setup. Haven't dug into it just yet. For now one can change the point at the start of the shader code, though (relativePos constant).



If you're interested in SDFs I recommend this as a starting point: Distance functions. Quílez has more to offer, of course. He is also quite active on shadertoy. Rewriting classic games for instance. Wow.

PS: Oh my, there are even sound shaders : https://www.shadertoy.com/view/MtGSWc


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By IGotProblems
      I am curious, if anyone would be interested in an RPG Adventure in a visual novel art style?
      I loved Doki Doki and if I could create something in an RPG element, that would be THE BEST.
      I am a complete noob however, that is the thing. Like I just started adventuring into coding two weeks ago. I love it so far.
      I think I may be addicted. oof. Which is why I want to create something I have that itch. lol.
      Basically if anyone who wants to pitch in for free, or not I'd be glad to include them in the credits section.
      Also, I'd love to get the community involved in this, to create more fun RPG-esk things if that makes sense.
      Where would I go for that?
    • By TheMode
      Before starting: I'm looking for a Java developer who want to help me improving my game engine and then, create a game that I will describe 
      My goal is to create a "fight arena" multiplayer game similar to xblaster (only how they managed arena, I do not want robot stuff) 
      For people who do not know this game, let me explain how I'm inspired by it
      You log in the game, you can enter an arena at any time, in the arena, there are 4 portals where you can go and enter another arena, there can be a maximum of 4players in the same arena, they have to fight each other in order to get money to improve their characters. 
      I won't describe it any longer, I've much more ideas about the game. 
      I already done the server architecture, I have a Game Engine (the client side), but there are still things to do on it, that's why I'm looking for another developer to help me if the game also look interesting for you
      Here the version of the engine:
      Discord: TheMode#3487
    • By babaliaris
      My texture problems just don't want to stop keep coming...
      After a lot of discussions here with you guys, I've learned a lot about textures and digital images and I fixed my bugs. But right now I'm making an animation system and this happened.
      Now if you see, the first animation (bomb) is ok. But the second and the third (which are arrows changing direction) are being render weird (They get the GL_REPEAT effect).
      In order to be sure, I only rendered (without using my animation system or anything else i created in my project, just using simple opengl rendering code) the textures that are causing this issue and this is the result (all these textures have exactly 115x93 resolution)

      I will attach all the images which I'm using.
      giphy-27 and giphy-28 are rendering just fine.
      All the others not.They give me an effect like GL_REPEAT which I use in my code. This is why I'm getting this result? But my texture coordinates are inside the range of -1 and 1 so why?
      My Texture Code:
      #include "Texture.h" #include "STB_IMAGE/stb_image.h" #include "GLCall.h" #include "EngineError.h" #include "Logger.h" Texture::Texture(std::string path, int unit) { //Try to load the image. unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &m_channels, 0); //Image loaded successfully. if (data) { //Generate the texture and bind it. GLCall(glGenTextures(1, &m_id)); GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); //Not Transparent texture. if (m_channels == 3) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data)); } //Transparent texture. else if (m_channels == 4) { GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); } //This image is not supported. else { std::string err = "The Image: " + path; err += " , is using " + m_channels; err += " channels which are not supported."; throw VampEngine::EngineError(err); } //Texture Filters. GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); //Generate mipmaps. GLCall(glGenerateMipmap(GL_TEXTURE_2D)); } //Loading Failed. else throw VampEngine::EngineError("There was an error loading image \ (Myabe the image format is not supported): " + path); //Unbind the texture. GLCall(glBindTexture(GL_TEXTURE_2D, 0)); //Free the image data. stbi_image_free(data); } Texture::~Texture() { GLCall(glDeleteTextures(1, &m_id)); } void Texture::Bind(int unit) { GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); }  
      My Render Code:
      #include "Renderer.h" #include "glcall.h" #include "shader.h" Renderer::Renderer() { //Vertices. float vertices[] = { //Positions Texture Coordinates. 0.0f, 0.0f, 0.0f, 0.0f, //Left Bottom. 0.0f, 1.0f, 0.0f, 1.0f, //Left Top. 1.0f, 1.0f, 1.0f, 1.0f, //Right Top. 1.0f, 0.0f, 1.0f, 0.0f //Right Bottom. }; //Indices. unsigned int indices[] = { 0, 1, 2, //Left Up Triangle. 0, 3, 2 //Right Down Triangle. }; //Create and bind a Vertex Array. GLCall(glGenVertexArrays(1, &VAO)); GLCall(glBindVertexArray(VAO)); //Create and bind a Vertex Buffer. GLCall(glGenBuffers(1, &VBO)); GLCall(glBindBuffer(GL_ARRAY_BUFFER, VBO)); //Create and bind an Index Buffer. GLCall(glGenBuffers(1, &EBO)); GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)); //Transfer the data to the VBO and EBO. GLCall(glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW)); GLCall(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW)); //Enable and create the attribute for both Positions and Texture Coordinates. GLCall(glEnableVertexAttribArray(0)); GLCall(glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)0)); //Create the shader program. m_shader = new Shader("Shaders/sprite_vertex.glsl", "Shaders/sprite_fragment.glsl"); } Renderer::~Renderer() { //Clean Up. GLCall(glDeleteVertexArrays(1, &VAO)); GLCall(glDeleteBuffers(1, &VBO)); GLCall(glDeleteBuffers(1, &EBO)); delete m_shader; } void Renderer::RenderElements(glm::mat4 model) { //Create the projection matrix. glm::mat4 proj = glm::ortho(0.0f, 600.0f, 600.0f, 0.0f, -1.0f, 1.0f); //Set the texture unit to be used. m_shader->SetUniform1i("diffuse", 0); //Set the transformation matrices. m_shader->SetUniformMat4f("model", model); m_shader->SetUniformMat4f("proj", proj); //Draw Call. GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL)); }  
      Vertex Shader:
      #version 330 core layout(location = 0) in vec4 aData; uniform mat4 model; uniform mat4 proj; out vec2 TexCoord; void main() { gl_Position = proj * model * vec4(aData.xy, 0.0f, 1.0); TexCoord = aData.zw; }  
      Fragment Shader:
      #version 330 core out vec4 Color; in vec2 TexCoord; uniform sampler2D diffuse; void main() { Color = texture(diffuse, TexCoord); }  

    • By babaliaris
      For those who don't know me I have started a quite amount of threads about textures in opengl. I was encountering bugs like the texture was not appearing correctly (even that my code and shaders where fine) or I was getting access violation in memory when I was uploading a texture into the gpu. Mostly I thought that these might be AMD's bugs because when someone was running my code he was getting a nice result. Then someone told me "Some drivers implementations are more forgiven than others, so it might happen that your driver does not forgive that easily. This might be the reason that other can see the output you where expecting". I did not believe him and move on.
      Then Mr. @Hodgman gave me the light. He explained me somethings about images and what channels are (I had no clue) and with some research from my perspective I learned how digital images work in theory and what channels are. Then by also reading this article about image formats I also learned some more stuff.
      The question now is, if for example I want to upload a PNG to the gpu, am I 100% that I can use 4 channels? Or even that the image is a PNG it might not contain all 4 channels (rgba). So I need somehow to retrieve that information so my code below will be able to tell the driver how to read the data based on the channels.
      I'm asking this just to know how to properly write the code below (with capitals are the variables which I want you to tell me how to specify)
      stbi_set_flip_vertically_on_load(1); //Try to load the image. unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &m_channels, HOW_MANY_CHANNELS_TO_USE); //Image loaded successfully. if (data) { //Generate the texture and bind it. GLCall(glGenTextures(1, &m_id)); GLCall(glActiveTexture(GL_TEXTURE0 + unit)); GLCall(glBindTexture(GL_TEXTURE_2D, m_id)); GLCall(glTexImage2D(GL_TEXTURE_2D, 0, WHAT_FORMAT_FOR_THE_TEXTURE, m_width, m_height, 0, WHAT_FORMAT_FOR_THE_DATA, GL_UNSIGNED_BYTE, data)); } So back to my question. If I'm loading a PNG, and tell stbi_load to use 4 channels and then into glTexImage2D,  WHAT_FORMAT_FOR_THE_DATA = RGBA will I be sure that the driver will properly read the data without getting an access violation?  
      I want to write a code that no matter the image file, it will always be able to read the data correctly and upload them to the GPU.
      Like 100% of the tutorials and guides about openGL out there (even one which I purchased from Udemy) where not explaining all these stuff and this is why I was experiencing all these bugs and got stuck for months!
      Also some documentation you might need to know about stbi_load to help me more:
      // Limitations: // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 // stbi_image_free(data)  
    • By CSharpCoder
      In MonoGame I'm writing a shader for lighting and shadows in a 2D Platformer.

      A shadow will be drawn for each character for each light that hits said character. Because shadows from different lights can overlap the shadows are drawn to a texture where each pixel is a bitfield where each bit tells you if the pixel was reflected by a given light. In the lighting shader for each light, it only applies light if the bit for that light is not set at the given pixel.

      In order to not make for example 40 draw calls to draw 40 shadows if 40 lights overlapped a character, I batch shadows together into a VertexBuffer with the data specifying which light created the given shadow. In the shadow shader, it samples the render target I am drawing the shadows to and sets its own bit. My problem is that the changes from the previous shadows in the same batch aren't applied to the render target until after the draw call has completed. This results in the bitfield getting overwritten by shadows from other lights.

      If I could somehow sample the back buffer this wouldn't be a problem. Is there any way I can fix this without making a draw call for each shadow?

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!