Hacker News new | ask | show | jobs
by datenwolf 3705 days ago
> when this task used to be as simple as:

a) Immediate Mode has been out of fashion for since before the turn of the century. Even the good old NVidia GeForce2 of 1998 already had OpenGL extensions that are essentially vertex buffer objects; you can in fact use the GL_ARB_vertex_buffer_object extension on a GeForce2. If you don't believe me, I'm currently headed to my mother's and I have a old box stored in her cellar with a GeForce2 in it; I could give interested parties a SSH into it (you just have to live with a old Gentoo installation that I didn't touch for some 10 years or so).

b) There's a certain complexity cutoff where the whole shader loading boilerplate plus shader code is less, than what it takes to setup an equivalent fixed function pipeline setup. Making an educated estimation I'd say, that the break even point is, when enabling a register combiner on two textures, one a cube map, the other a normal+shininess map, setting DOT3 combining of normal with vertex secondary color (for normal mapping), directing the normal map into the cubemap texture coordinate lookup and fading between reflection and dull shader based on shininess. Sounds complex? Indeed it is, but keep in mind that this kind of thing was already possible with the GeForce3 (at least, I think it may even work on the GeForce2, but after over 14 years since writing the last time a program targeting it, I'm a bit shaky on the details).

Anyway, to set up this kind of register combining you'd need between 4 to 5 calls of glTexEnvi per texture. Another 3 glTexEnvi calls for setting up the secondary color muxing mode and another 3 calls for setting the final stage fading mode. Add to that the hours of twisting your brain to figure trying to wrap your mind around all the relevant state switches in the register combiners.

With shaders you simply write down what you want.

1 comments

Yes, I'm aware of how both immediate mode and vertex arrays work in OpenGL.

The problem, and this is even getting away from what the article addresses, is that a green developer approaching the application must learn an entirely new language (shaders) before drawing a single primitive. In mattbee's example, what is the beginner supposed to think of the line "#version 410" or "in vec3 vp" which is not even C code?

I'm also not saying that all of the legacy functions from the fixed-function pipeline should be maintained, with all of their specific parameters, but that those attributes should be bound with a default shader that supports all of the same capabilities. So, using GLSL attribute accessors with each platform having the same default shader (and default parameter names). Then, wrap the IM example in arrays, call a few binds, and make a draw call. That's much simpler than writing two embedded programs inside your first program. I think it would have been very beneficial if OpenGL ES had been rolled out with a design like this.

Maybe it's because graphics programming is so feature focused that it creates a problem. For me, the issue is the obstacles between getting shapes on the screen, especially in contexts like WebGL or mobile.

The beginner needs to rise to the task.

GLSL isn't complex at all, least of all because its problem domain is basically pure math.

It should take less than an afternoon to understand how to write a shader if they have any familiarity with math.

If they don't know math, they shouldn't be doing shader programming until they learn.

Writing a fixed function shader is to learning graphics programming what writing a Makefile is to learning C++. I'm not saying it's not important, or not important in the long run, but what introductory programming book would start out showing how to use a linker instead of how to write a program? In this case things are compounded by the fact that there is no linker, which means the programmer has to shuffle around string programs at runtime.

I agree, though, that shader writing should help with learning 3D math. Saying GLSL is not complex is a bit of an overstatement, though. The language is simple, but the fact is that GLSL doesn't behave like C with certain statements, and not knowing everything will make things complicated.

You've also described a chicken-and-egg problem. Can't make a 3D app till you learn GLSL, can't use GLSL until you make a 3D app.

It also feels absurd to be writing any static language code wrapped in a string, in JavaScript, to be sent to a GPU and then compiled. The user doesn't have newlines, let alone syntax highlighting. Sure, most WebGL developers will just use Three.js but then they aren't really learning anything, anyway, and we still have these patchwork solutions.

I disagree that it's inappropriate for beginners, and comparing it to a build system is incorrect.

Shaders are a step in the pipeline. Beginners should be learning the pipeline:

1. Geometry is instantiated and draw parameters set.

2. Geometry vertices are transformed by a vertex shader.

3. Transformed vertices in primitive are used to sample across its geometry, producing pixel fragments.

4. Pixel fragments are transformed by a fragment shader.

5. Transformed fragments are written to a framebuffer.

That's the whole thing, and GLSL neatly handles 2-4. Step 1 is a kinda pain in the ass, but not terrible. Step 5 is usually simple, but no worse than 1. We shouldn't be protecting users from dealing with the (simple) facts of life in a graphics pipeline.

As for Javacsript not having multi-line strings...that's hardly the fault of WebGL, and honestly the simplicity of passing around strings means that as the language gets more interesting support (for interpolations and whatever) the API will be unaffected.

It's a stretch to say that GLSL shaders are broken, since they are functional and every OpenGL application uses them, but it's hard to say that they are implemented in a logical way as they are now, especially at a topical glance.
> but what introductory programming book would start out showing how to use a linker instead of how to write a program?

A good one!

Just take a glance over at StackOverflow and consider how many questions there essentially boil down to lack of understanding how the pieces in the compiler toolchain fit together:

http://lmgtfy.com/?q=stackoverflow.com%20undefined%20referen...

In workshop class a teacher will also start with explaining how to properly handle a tool before showing how to apply said tool to a problem.

You're skirting around the issue.

The original comment was - "When writing my first OpenGL code, I was stunned by the amount of boilerplate required."

Your response was - "the amount of boilerplate required ... is minimal."

It's clearly not minimal because we've outlined ways it could be reduced. Your counter-example of writing larger programs doesn't solve the issue of drawing a single triangle. Don't confuse a local minima for a global minima and don't confuse your experience with every noobie's experience.

Although I would say it looks minimal compared to an early D3D application.

> You're skirting around the issue.

Sorry, I didn't mean to.

> The original comment was - "When writing my first OpenGL code, I was stunned by the amount of boilerplate required."

Well, you have to consider what the minimum requirements are to draw something at all. Not just at a computer display, but also in the physical world.

You need a canvas to draw on (window). You have to prepare your drawing tools (GL context). You have to sketch out what you want to draw. You have to prepare your paint (either setting up the OpenGL fixed function pipeline) and last but not least to the actual drawing.

Old style OpenGL (fixed function pipeline with immediate mode vertex calls) and modern OpenGL (shader based) pretty much even out in the amount of characters to type for drawing a triangle (fixed function slightly wins if using client side vertex arrays). The main difference is, that the fixed function pipeline gives you a default state that will show you something. But that's something that only OpenGL does and you'll find in no other graphics system (and I include also all offline renderers there).

> It's clearly not minimal because we've outlined ways it could be reduced

But this reduction works only for the most simple most basic images drawn. As soon as you're drawing something slightly more complex than a RGB triangle the shader based approach quickly wins out. The very first shader languages were created not for programmable hardware (didn't exist yet) but to kill the dozenz of lines of OpenGL code required to setup a register combiner and replace it with an API that'd allow the programmer to write down the intention in a high level manner as mathematical expressions.

Also lets not forget that if you were really after simplifying things for newbies, the right approach would be providing a library that does simple shader setup. Completely ignoring OpenGL for a moment, you'd hardly put a newbie through the paces of touching directly the native APIs to setup a GL context (well, okay, that was how I learnt it back then, because I couldn't make GLUT work with my compiler back then). So maybe provide something like a GLU-2 or GLU-3 for that; the GLU spec has not been maintained for two decades.

I feel this discussion is moot (regarding OpenGL) until everybody participating has implemented at least one complex OpenGL scene done with just register combiners. I'll throw in my purely register combiners based water ripples effect I implemented some 16 years ago (have the sources for that somewhere, but if I don't find them I can reimplement) and compare that with the straightforwardness that is a shader based approach.