I added an object with a MeshPhongMaterial to my scene. I want to spy on the uniforms that Three is setting on the object. I thought it would be as simple as obj.uniforms.uniformName, but that doesn't actually exist. Is there a simple way to get the runtime uniforms Three is setting?
What I'm after is the uniform names and their values.
The method I came up with requires a double loop. The only thing I can see on a running material that's useful is material.uniformsList which is a list of WebGLUniformLocations and values, but no names. There is also material.program.uniforms which is a key value store of names to WebGLUniformLocations. It seems like you can zip the two together to get a uniform list, but is there a better way?
let builtUniforms = {};
// For every uniform in the array, look for its location in the program
// uniforms to get the name
for( x = 0; uniform = object.material.uniformsList[ x++ ]; ) {
// uniform is [ { type, value }, WebGLUniformLocation ] but no name :(
for( uniformName in object.material.program.uniforms ) {
// If the WebGLUniformLocations match up, we've found the name
if( object.material.program.uniforms[ uniformName ] === uniform[ 1 ] ) {
builtUniforms[ uniformName ] = {
value: uniform[ 0 ].value,
name: uniformName
};
break;
}
}
}
Is there a more straightforward way to get the uniform names and values from a Three.Material?
Will the uniformsList or program.uniforms ever change? Do I need to build this object once per render loop, or can I build it once and resuse it?
Three stores the key value store you're looking for in material.__webglShader.uniforms
Also look in THREE.UniformsLib
and also THREE.ShaderLib - e.g. THREE.ShaderLib.phong
these contain the templates for built-in uniforms and also the shaders, like MeshPhongMaterial, which use them.
Related
I am wondering if it is possible to get access to aggregated data from a deck.gl layer to be able to draw a legend.
Because the colour scheme is supplied I would only require the extent of the aggregated values calculated by the screengrid layer to be able to add this to the legend.
I know there are tooltips, but in some circumstances it would be nice to have access to these values.
I'm using the HexagonLayer and for that one you can find the values for the layers by using a semi custom onSetColorDomain function when initializing your layer. Then save the domain range array to a variable and call a make legend function.
for example:
const hexlayer = new HexagonLayer({
id: 'heatmap',
pickable: true,
colorRange: COLOR_RANGE,
data: feats_obj.coords_array,
elevationScale: 9,
extruded: true,
radius: 300,
getColorValue: points => {
if( points.length > max_points) {
max_points = points.length
}
renderCategories( incident_categories )
return points.length
},
onSetColorDomain: (ecol) => {
console.log('color domain set', ecol)
max_span.innerHTML = ecol[1]
color_domain = ecol;
create_legend()
// console.log('max_points: ', max_points)
},...
})
The hacky way i figured out was to make a max points in a polygon global variable outside initializing the layer and have it update the value anytime there's a polygon with more points in it than that max value. An example of it in the wild is: https://mpmckenna8.github.io/sfviz/?start_date=2020-05-01&end_date=2020-05-31 with a link to the repo u can hack on there.
I have several objects grouped in a Object3D. I wanna calculate the boundingbox of the whole group, except some specific objects in that group.
Can you disable the calculation of boundingbox for those objects?
Can you disable the calculation of boundingbox for those objects?
If you are using Box3.setFromObject() then no, this is not possible. The code processes all children of the hierarchy and expands the AABB if an object has a geometry property.
three.js R101
As Mugen mentioned it's not possible to do this out of the box but you can achieve it by manually traversing the tree.
Here's an idea for how you might do that.
var box = null;
group.traverse(c => {
// logic for whether or not to include the child
var includeChild = c.isMesh;
if (includeChild) {
// initialize the box to the first valid child found
// otherwise expand the bounds
if (box === null) {
box = new THREE.Box3();
box.setFromObject(c);
} else {
box.expandByObject(c);
}
}
});
You can change the boolean logic for includeChild to determine whether or not you want an object to be included in the bounds calculations or not.
Hope that helps!
I want to implement per-object motion-blur effect based on calculating previous pixel position inside shaders.
This technic's first step is to build velocity map of moving objects. This step requirements is to have as uniform variables projection and model view matrices of current frame and the same matrices of previous frame.
How could I include those matrices to uniforms for some special shader? I supposed to have solution in some way like:
uniforms = {
some_uniform_var : {type: "m4", value: initialMatrix, getter: function(){
// `this` points to object
return this.worldMatrix
}}
}
But now in THREE.js this is not available. We could make some sort of monkey patching, but I cannot find best way to do it.
Any suggestions?
The current solvation to this problems consist of several parts. I'm using EffectComposer to make several passes of rendered scene, one of then - VelocityPass. It takes current and previous model-view matrix and projection matrix and produces two positions. Both of them then used to calculate speed of a point.
Shader looks like this
"void main() {",
"vec2 a = (pos.xy / pos.w) * 0.5 + 0.5;",
"vec2 b = (prevPos.xy / prevPos.w) * 0.5 + 0.5;",
"vec2 oVelocity = a - b;",
"gl_FragColor = vec4(oVelocity, 0.0, 1.);",
"}"
There're several issues of this decision.
Three.js has certain point where it injects matrices to object-related shaders. The very ending of SetProgram closure, which lives in WebGLRenderer. That's why I took the whole renderer file, renamed renderer to THREE.MySuperDuperWebGLRenderer and added couple lines of code in it:
A closure to access closures, defined in userspace:
function materialPerObjectSetup(material, object){
if( material.customUniformUpdate ){
material.customUniformUpdate( object, material, _gl ); // Yes, I had to pass it...
}
}
And calling of it in renderBuffer and renderBufferDirect;
var program = setProgram( camera, lights, fog, material, object );
materialPerObjectSetup(material, object);
Now - the userspace part:
velocityMat = new THREE.ShaderMaterial( THREE.VelocityShader );
velocityMat.customUniformUpdate = function(obj, mat, _gl){
// console.log("gotcha");
var new_m = obj.matrixWorld;
var p_uniforms = mat.program.uniforms;
var mvMatrix = camera.matrixWorldInverse.clone().multiplyMatrices(camera.matrixWorldInverse, obj._oldMatrix );
_gl.uniformMatrix4fv( p_uniforms.prevModelViewMatrix, false, mvMatrix.elements );
_gl.uniformMatrix4fv( p_uniforms.prevProjectionMatrix, false, camera.projectionMatrix.elements );
obj._pass_complete = true; // Необходимо сохранять состояние старой матрицы пока не отрисуется этот пасс.
// А то матрицы обновляются каждый рендеринг сцены.
}
_pass_complete needed when we rerendering scene several times - each time matrix recalculated. This trick help us save previous matrix untill we use it.
_gl.uniformMatrix4fv is needed, because three.js serves universes one time before rendering. No matter how much objects we have - other method will pass to the shader modelViewMatrix of the last one. This happens because I want to draw this scene fully using VelocityShader. There's no other way to say to Renderer to use some alternative material for objects.
And as final point of this explaination I putting here a trick to manage previous matrix of an object:
THREE.Mesh.prototype._updateMatrixWorld = rotatedObject.updateMatrixWorld;
THREE.Mesh.prototype._pass_complete = true;
Object.defineProperty(THREE.Mesh.prototype, "updateMatrixWorld", {get: function(){
if(this._pass_complete){
this._oldMatrix = this.matrixWorld.clone();
this._pass_complete = false;
}
this._updateMatrixWorld();
return (function(){
});
}})
I believe, that there's could be a nicer solution. But sometimes I need to act in rush. And such kind of monkey things could happen.
I'm trying to check inside the shader (GLSL) if my vec4 is NULL. I need this for several reasons, mostly to get specific graphics cards compatible, since some of them pass a previous color in gl_FragColor, and some don't (providing a null vec4 that needs to be overwritten).
Well, on a fairly new Mac, someone got this error:
java.lang.RuntimeException: Error creating shader: ERROR: 0:107: '==' does not operate on 'vec4' and 'int'
ERROR: 0:208: '!=' does not operate on 'mat3' and 'int'
This is my code in the fragment shader:
void main()
{
if(gl_FragColor == 0) gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); //Line 107
vec4 newColor = vec4(0.0, 0.0, 0.0, 0.0);
[...]
if(inverseViewMatrix != 0) //Line 208
{
[do stuff with it; though I can replace this NULL check with a boolean]
}
[...]
gl_FragColor.rgb = mix(gl_FragColor.rgb, newColor.rgb, newColor.a);
gl_FragColor.a += newColor.a;
}
As you can see, I do a 0/NULL check for gl_FragColor at the start, because some graphics cards pass valuable information there, but some don't. Now, on that special mac, it didn't work. I did some research, but couldn't find any information on how to do a proper NULL check in GLSL. Is there even one, or do I really need to make separate shaders here?
All variables meant for reading, i.e. input variables always deliver sensible values. Being an output variable, gl_FragColor is not one of these variables!
In this code
void main()
{
if(gl_FragColor == 0) gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); //Line 107
vec4 newColor = vec4(0.0, 0.0, 0.0, 0.0);
The very first thing you do is reading from gl_FragColor. The GLSL specification clearly states, that the value of an output varialbe as gl_FragColoris, is undefined when the fragment shader stage is entered (point 1):
The value of an output variable will be undefined in any of the three following cases:
At the beginning of execution.
At each synchronization point, unless
the value was well-defined after the previous synchronization
point and was not written by any invocation since, or
the value was written by exactly one shader invocation since the
previous synchronization point, or
the value was written by multiple shader invocations since the
previous synchronization point, and the last write performed
by all such invocations wrote the same value.
When read by a shader invocation, if
the value was undefined at the previous synchronization
point and has not been writen by the same shader invocation since, or
the output variable is written to by any other shader
invocation between the previous and next synchronization points,
even if that assignment occurs in code following the read.
Only after an element of an output variable has been written to for the first time its value is defined. So the whole thing you do there makes no sense. That it "didn't work" is completely permissible and an error on your end.
You're invoking undefined behaviour and technically it would be permissible for your computer to become sentinent, chase you down the street and erase all of your data as an alternative reaction to this.
In GLSL a vec4 is a regular datatype just like int. It's not some sort of pointer to an array which could be a null pointer. At best it has some default value that's not being overwritten by a call to glUniform.
Variables in GLSL shaders are always defined (otherwise, you'll get a linker error). If you don't supply those values with data (by not loading the appropriate uniform, or binding attributes to in or attribute variables), the values in those variables will be undefined (i.e., garbage), but present.
Even if you can't have null values, you can test undefined variables. This is a trick that I use to debug my shaders:
...
/* First we test for lower range */
if(suspect_variable.x < 0.5) {
outColour = vec4(0,1,0,0); /* Green if in lower range*/
} else if(suspect_variable.x >= 0.5) { /*Then for the higher range */
outColour = vec4(1,0,0,0); /* Red if in higher range */
} else {
/* Now we have tested for all real values.
If we end up here we know that the value must be undefined */
outColour = vec4(0,0,1,0); /* Blue if it's undefined */
}
You might ask, what could make a variable undefined? Out of range access of an array would cause it to be undefined;
const int numberOfLights = 2;
uniform vec3 lightColour[numberOfLights];
...
for(int i = 0; i < 100; i++) {
/* When i bigger than 1 the suspect_variable would be undefined */
suspect_variable = suspect_variable * lightColour[i];
}
It is a simple and easy trick to use when you do not have access to real debugging tools.
I used a map<CString, vector<double>> structure to store the mapping of file name to its HSV color histogram.And there are 100 elements in this map as a image DB.If now comes a image,and I have get the input image's histogram,how can I do the compare?
I know a method called "quadratic distance", but I do not understand it.
One simple method would be using a distance calculator like this:
double dist(vector<double> *histogram1, vector<double> *histogram2) {
double result = 0.0;
for (vector<double>::iterator val1=histogram1->begin(), val2=histogram2->begin();
val1<histogram1->end();
val1++, val2++) {
result += (*val1 - *val2) * (*val1 - *val2);
}
result = sqrt(result);
return result;
}
And then determine which histogram has the smallest distance. Please note that this is for
demonstration purposes only, you must add vector size checks etc.