Genau diese Einschränkungen waren für mich damals der Grund, diese Rendering-Library zu basteln!
Der Shader kann frei programmiert werden. Jeder kann beliebige Uniforms und Attributes definieren und verwenden. Das ist faktisch die Schnittstelle. Bei Code wie
attribute vec3 positions;
attribute vec3 normals;
uniform float intensity;
void main() { .... }
muss man in GL grundsätzlich (und hier im Pseudocode, um zu verdeutlichen, dass das sprachunabhängig ist) sowas machen wie
setAttribute(getLocation(shader, "positions"), positionsBufferId);
setAttribute(getLocation(shader, "normals"), normalsBufferId);
setUniform(getLocation(shader, "intesity"), intensity);
Und das ist ein Krampf. Und das Problem ist: Es funktioniert nicht. Mist, woran liegt das jetzt? Naja, ganz einfach: Die Uniform heißt "intensity"
, und nicht "intesity"
Das Problem ist ja gerade, dass zwischen dem Shader-Code und dem Host-Code keine Verbindung besteht. Die Information über die Attributes und Uniforms, die im Shader vorkommen, ist auf der Host-Seite nicht vorhanden. Die Einzige (theoretische!) Möglichkeit, dort „programmatisch“ ranzukommen, wäre, den Shadercode zu parsen, und im AST dann die UniformVariableDeclaration
-Objekte zu untersuchen, um rauszufinden, wie die Variablen heißen.
Die idealistische Vorstellung, einen Shader mit sowas wie
float positions[] = ...;
float normals[] = ...;
float intensity = 41.999f;
callShader(shader, positions, normals, intensity);
aufrufen zu können, scheitert gerade an dieser fehlenden Verbindung, bzw. daran, dass das schon von der darunter liegenden Schicht (also OpenGL) nicht in dieser Form angeboten wird.
Aber selbst wenn man das alles wüßte, dann wüßte man nichts über die Semantik der Shader-Inputs. Die Verdrahtung wird immer von demjenigen gemacht werden müssen, der weiß, welchen Shader er lädt, welche Uniforms+Attributes der Shader hat, und mit welchen Daten die gefüttert werden müssen.
(Kleiner Abstecher: Die Schwierigkeiten, die damit verbunden sind, wurden schon an anderer Stelle deutlich. Das ganze Konzept der technique
-Objekte in glTF ist ein Versuch, dieses Problem zu lösen. Eigentlich war das „ganz gut“, im Rahmen dessen, was überhaupt möglich ist. Leider wird das ganze mit glTF 2.0 zu einer Extension - ein Tribut an Vulkan, DirectX und andere Graphics-APIs…)
<selfPromotion>
Die Möglichkeit, diese Verdrahtung flexibel zu machen, war die Intention hinter der connect
-Methode die ich in der Rendering-Library gebastelt hatte. Die bekommt die Information über den Parameter
(d.h. den Shaderparameter), und das Attribute
(d.h. die Daten, die für diesen Parameter bestimmt sind). Man kann da entweder defaults verwenden,
builder.connect(Parameters.VERTEX_POSITION, Attributes.VERTICES);
oder was spezielles, eigenes. Wenn man also einen Shader hat, der ein Attribute vec3 mySpecificStuff
erwartet, dann kann man die Daten für mySpecificStuff
in ein GraphicsObject
legen, und dann eben sagen
builder.connect(
Parameters.create("mySpecificStuff", ParameterType.TUPLE3F)
Attributes.create("mySpecificStuff", DataBufferType.FLOAT, 3));
womit dann in Zukunft bei jedem Programmaufruf die Attribute-Location-Jongliererei intern übernommen und die Daten für den eigentlichen Rendering-Call vorbereitet werden.
Aber das alles war ziemlich „naiv“, und ich bin nicht auf dem neuesten Stand von allen Details - UBOs und layout-specifier in Shadern … und … ausführlich „getestet“ ist das auch nicht, d.h. es gibt sicher vieles, was damit nur unzureichend abgebildet werden kann.
</selfPromotion>