How do you apply different shaders to an object?

Started by
6 comments, last by taby 2 months, 1 week ago

I thought shaders would work in a cascading effect where I can apply a shader during a render pass that affects everything and then when I render the scene I can apply another shader that's associated with the object. So I'd first set the depth shader and set it's uniforms and then in a for loop where I render the scene I'd grab another shader that's stored in the object this way I could have a rock object use a simple static shader and then a animated character uses some dynamic/skeleton shader.

void RenderPass() {
	// Bind the shadow shader
	// Set uniforms
	// ...

	// Render scene objects
	for (Object sceneObj : sceneObjs) {
		if sceneObject is static {
			// Apply static shader object
			// Set uniforms
			// ...
		}
		else if sceneObject is dynamic{
			// Apply dynamic shader object
			// Set uniforms
			// ...
		}
	}
}

Well unsurprisingly I was wrong it just overwrites the initial shader. The only alternatives I can think of is the shader needs to support all types of objects and then I'd can control what it outputs via materials either that or I render the scene twice in a pass (not sure if this would work either)

Advertisement

Not sure i understand your problem, but maybe you want to bin / sort your objects by shader. This reduces overhead from switching shaders often and helps performance, but conflicts with the other optimization objective of rendering objects in front to back order to minimize overdraw.
Usually the priority is to sort by material first, afaict.

It also depends on forward vs. deferred and related techniques.

Multi pass rendering may be needed for special materials, e.g. transparency, which often requires back to front order and forward shading, even if the engine uses mainly deferred otherwise.

@JoeJ

Not sure i understand your problem

Sorry I don't know the best way to phrase it, but say I wanted to make a tree that has a shadow and wind effects or a character that has a shadow and animation to my understanding this would require multiple shaders I suppose you could make one monolithic one, but ehhh anyways I can't have multiple shaders applied at once (which is what I originally though) and switching between shaders would cause the previous to be discarded so what do I do? The only alternative I could think of is shaders would require information from previous shaders or would just have duplicate code, but I don't think either are very good solutions.

Sorting objects by shader reduces overhead from switching shaders often but conflicts with the optimisation of rendering objects in front to back order. Priority is to sort by material first. Special materials may require multi pass rendering even if the engine uses mainly deferred.

Hope this makes sense

Konjointed said:
Sorry I don't know the best way to phrase it, but say I wanted to make a tree that has a shadow and wind effects or a character that has a shadow and animation to my understanding this would require multiple shaders I suppose you could make one monolithic one

The shadow part is confusing.

The standard is to render the character twice.
Once to the shadow map, requiring it's own projection from the light source. Using the same vertex shader for skinning, but with a simple pass thru pixel shader.
And a second time to the frame buffer, using camera projection, the vertex shader but a complex pixel shader for materials.

So you must render multiple times. Once for each light where the character casts shadow, and for the frame buffer. There is no way around that.
In theory it is possible to skin the vertices just once and then render to multiple viewports, e.g. using geometry shader. But this is not very practical. Because the character might be not visible from some viewports, the approach still generates overhead and redundant processing, afaik.

But it is pretty common to skin vertices just once with a compute shader, store the transformed vertices to VRAM, and then render to multiple viewports no longer needing to transform multiple times.
This approach is usually worth it if we need to transformed vertices for some other things as well, e.g. fine grained occlusion culling of small clusters over any mesh. Typically used in GPU driven rendering approaches. Or, if we use ray tracing, we must pre-transform this way so the BVH can be updated from known vertex positions.

But that's probably not what you mean, i guess.

Konjointed said:
I can't have multiple shaders applied at once (which is what I originally though) and switching between shaders would cause the previous to be discarded so what do I do?

No, you can't have multiple shaders from the same shader stage applied to the same draw call.
If you need this, you have to create a new shader combining both functionalities into a new single one manually.

There also is no kind of shader stack, which remembers former active shaders and automatically selects the last used one if you disable the current. If you need such functionality, you need to implement and manage it yourself. (Idk which API you use, but this applies to any API afaict.)

But why would you want to have this at all? What is the problem you try to solve?

Konjointed said:
I could think of is shaders would require information from previous shaders or would just have duplicate code, but I don't think either are very good solutions.

There are no function calls in any gfx API. CUDA or OpenCL can do this, but sadly game devs are left behind.
So until they change this and give us an update, duplicating code is our only option.

Though, this is usually only a problem with general purpose compute programs, not for rendering. So i wonder what you try to do.

Konjointed said:
Well unsurprisingly I was wrong it just overwrites the initial shader.

I believe that what you want is blending.

This reminds me of deferred shading:

https://en.wikipedia.org/wiki/Deferred_shading

This topic is closed to new replies.

Advertisement