Black screen when trying to use multiple textures in Expo - opengl-es

I'm trying to use multiple textures in expo, but for some reason whenever I try to bind more than one texture, even if the texture is unused in the shader, nothing renders to the screen.
Code that binds textures:
gl.useProgram(program);
gl.bindVertexArray(vao);
// Set texture sampler uniform
const TEXTURE_UNIT = 0;
const TEXTURE_UNIT2 = 1;
gl.uniform1i(uniformLocations.get('cameraTexture'), TEXTURE_UNIT);
gl.uniform1i(uniformLocations.get('segmentationTexture'), TEXTURE_UNIT2);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, cameraTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, segmentationTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, dims.width, dims.height);
gl.drawArrays(gl.TRIANGLES, 0, vertices.length / 2);
//console.log("draws")
gl.bindVertexArray(null);
gl.bindTexture(gl.TEXTURE_2D, null);
gl.useProgram(null);
Fragment shader:
#version 300 es
precision highp float;
uniform sampler2D cameraTexture;
uniform sampler2D segmentationTexture;
in vec2 uv;
out vec4 fragColor;
void main() {
fragColor = texture(cameraTexture, uv); //segmentationTexture.r *
}
Vertex Shader:
const horizontalScale = flipHorizontal ? -1 : 1;
return `#version 300 es
precision highp float;
in vec2 position;
in vec2 texCoords;
out vec2 uv;
void main() {
// Invert geometry to match the image orientation from the camera.
gl_Position = vec4(position * vec2(${horizontalScale}., -1.), 0, 1);
uv = texCoords;
}`
I have checked both textures by commenting out the code the binds the other texture, e.g.
const TEXTURE_UNIT = 0;
//const TEXTURE_UNIT2 = 1;
gl.uniform1i(uniformLocations.get('cameraTexture'), TEXTURE_UNIT);
//gl.uniform1i(uniformLocations.get('segmentationTexture'), TEXTURE_UNIT2);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, cameraTexture);
//gl.activeTexture(gl.TEXTURE1);
//gl.bindTexture(gl.TEXTURE_2D, segmentationTexture);
or
//const TEXTURE_UNIT = 0;
const TEXTURE_UNIT2 = 1;
//gl.uniform1i(uniformLocations.get('cameraTexture'), TEXTURE_UNIT);
gl.uniform1i(uniformLocations.get('segmentationTexture'), TEXTURE_UNIT2);
//gl.activeTexture(gl.TEXTURE0);
//gl.bindTexture(gl.TEXTURE_2D, cameraTexture);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, segmentationTexture);
and both render fine individually.
However, the act of trying to bind multiple textures, even though only one is used in frag shader, leaves me with only a black screen.

Related

Storing data as a texture for use in Vertex Shader for Instanced Geometry (THREE JS / GLSL)

I'm using a THREE.InstancedBufferGeometry, and I wish to access data in the Vertex Shader, encoded into a Texture.
What I want to do, is create a Data Texture with one pixel per instance, which will store position data for each instance (then at a later stage, I can update the texture using a simulation with a flow field to animate the positions).
I'm struggling to access the data from the texture in the Vertex Shader.
const INSTANCES_COUNT = 5000;
// FOR EVERY INSTANCE, GIVE IT A RANDOM X, Y, Z OFFSET, AND SAVE IT IN DATA TEXTURE
const data = new Uint8Array(4 * INSTANCES_COUNT);
for (let i = 0; i < INSTANCES_COUNT; i++) {
const stride = i * 4;
data[stride] = (Math.random() - 0.5);
data[stride + 1] = (Math.random() - 0.5);
data[stride + 2] = (Math.random() - 0.5);
data[stride + 3] = 0.0;
}
const offsetTexture = new THREE.DataTexture( data, INSTANCES, 1, THREE.RGBAFormat, THREE.FloatType );
offsetTexture.minFilter = THREE.NearestFilter;
offsetTexture.magFilter = THREE.NearestFilter;
offsetTexture.generateMipmaps = false;
offsetTexture.needsUpdate = true;
// CREATE MY INSTANCED GEOMETRY
const geometry = new THREE.InstancedBufferGeometry();
geometry.maxInstancedCount = INSTANCES_COUNT;
geometry.addAttribute( 'position', new THREE.Float32BufferAttribute([5, -5, 0, -5, 5, 0, 0, 0, 5], 3 )); // SIMPLE TRIANGLE
const vertexShader = `
precision highp float;
uniform vec3 color;
uniform sampler2D offsetTexture;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
varying vec3 vPosition;
varying vec3 vColor;
void main(){
vPosition = position;
vec4 orientation = vec4(.0, .0, .0, .0);
vec3 vcV = cross( orientation.xyz, vPosition );
vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
vec2 uv = position.xy;
vec4 data = texture2D( offsetTexture, uv );
vec3 particlePosition = data.xyz * 1000.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition + particlePosition, 1.0 );
}
`;
const fragmentShader = `
precision highp float;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
}
`;
const uniforms = {
size: { value: 1.0 },
color: {
type: 'c',
value: new THREE.Color(0x3db230),
},
offsetTexture: {
type: 't',
value: offsetTexture,
},
};
// CREATE MY MATERIAL
const material = new THREE.RawShaderMaterial({
uniforms,
vertexShader,
fragmentShader,
side: THREE.DoubleSide,
transparent: false,
});
scene.add(new THREE.Mesh(geometry, material));
At the moment it seems that the data from the image isn't accessible in the vertex shader (if I just set the vUv to vec2(1.0, 0.0), for example, and change the offset positions, nothing changes), and also I'm not sure how to go about making sure that the instance can reference the correct texel in the texture.
So, my two issues are:
1) How to correctly set the Data Image Texture, and access that data in the Vertex Shader
2) How to correctly reference the texel storing the data for each particular instance (e.g, instance 1000 should use vec2(1000,1), etc
Also, do I have to normalize the data (0.0-1.0, or 0–255, or -1 – +1)
Thanks
You need to compute some kind of an index into the texture per instance.
Meaning, you need an attribute that is going to be shared by each instance.
if your triangle is
[a,b,c]
your index should be
[0,0,0]
Lets say you have 1024 instances and a 1024x1 px texture.
attribute float aIndex;
vec2 myIndex = ((aIndex + 0.5)/1024.,1.);
vec4 myRes = texture2D( mySampler, myIndex);

Per instance UV texture mapping in Three.js InstancedBufferGeometry

I have a InstancedBufferGeometry made up of a single Plane:
const plane = new THREE.PlaneBufferGeometry(100, 100, 1, 1);
const geometry = new THREE.InstancedBufferGeometry();
geometry.maxInstancedCount = 100;
geometry.attributes.position = plane.attributes.position;
geometry.index = plane.index;
geometry.attributes.uv = plane.attributes.uv;
geometry.addAttribute( 'offset', new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 ) ); // an offset position
I am applying a texture to each plane, which is working as expected, however I wish to apply a different region of the texture to each instance, and I'm not sure about the correct approach.
At the moment I have tried to build up uv's per instance, based on the structure of the uv's for a single plane:
let uvs = [];
for (let i = 0; i < 100; i++) {
const tl = [0, 1];
const tr = [1, 1];
const bl = [0, 0];
const br = [1, 0];
uvs = uvs.concat(tl, tr, bl, br);
}
...
geometry.addAttribute( 'uv', new THREE.InstancedBufferAttribute( new Float32Array( uvs ), 2) );
When I do this, I don't have any errors, but every instance is just a single colour (all instances are the the same colour). I have tried changing the instance size, and also the meshes per attribute (which I don't fully understand, struggling to find a good explanation in the docs).
I feel like I'm close, but I'm missing something, so a point in the right direction would be fantastic!
(For reference, here are my shaders):
const vertexShader = `
precision mediump float;
uniform vec3 color;
uniform sampler2D tPositions;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec2 uv;
attribute vec2 dataUv;
attribute vec3 position;
attribute vec3 offset;
attribute vec3 particlePosition;
attribute vec4 orientationStart;
attribute vec4 orientationEnd;
varying vec3 vPosition;
varying vec3 vColor;
varying vec2 vUv;
void main(){
vPosition = position;
vec4 orientation = normalize( orientationStart );
vec3 vcV = cross( orientation.xyz, vPosition );
vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
vec4 data = texture2D( tPositions, vec2(dataUv.x, 0.0));
vec3 particlePosition = (data.xyz - 0.5) * 1000.0;
vUv = uv;
vColor = data.xyz;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position + particlePosition + offset, 1.0 );
}
`;
const fragmentShader = `
precision mediump float;
uniform sampler2D map;
varying vec3 vPosition;
varying vec3 vColor;
varying vec2 vUv;
void main() {
vec3 color = texture2D(map, vUv).xyz;
gl_FragColor = vec4(color, 1.0);
}
`;
As all my instances need to take the same size rectangular area, but offset (like a sprite sheet), I have added a UV offset and UV scale attribute to each instance, and use this to define which area of the map to use:
const uvOffsets = [];
for (let i = 0; i < INSTANCES; i++) {
const u = i % textureWidthHeight;
const v = ~~ (i / textureWidthHeight);
uvOffsets.push(u, v);
}
...
geometry.attributes.uv = plane.attributes.uv;
geometry.addAttribute( 'uvOffsets', new THREE.InstancedBufferAttribute( new Float32Array( uvOffsets ), 2 ) );
uniforms: {
...
uUvScale: { value: 1 / textureWidthHeight }
}
And in the fragment shader:
void main() {
vec4 color = texture2D(map, (vUv * uUvScale) + (vUvOffsets * uUvScale));
gl_FragColor = vec4(1.0, 1.0, 1.0, color.a);
}
\o/

Shader wireframe of an object

I want to see a wireframe of an object without the diagonals like
Currently, I add lines according to the vertices, the problem is after I have several of those I experience a major performance degradation.
The examples here are either too new for my version of Three or don't work (I commented there about it).
So I want to try to implement a shader instead.
I tried to use this shader: https://stackoverflow.com/a/31610464/4279201 but it breaks the shape to parts and I'm getting WebGL errors.
That's how I use it:
const vertexShader = `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`
const fragmentShader = `
#version 150 compatibility
flat in float diffuse;
flat in float specular;
flat in vec3 edge_mask;
in vec2 bary;
uniform float mesh_width = 1.0;
uniform vec3 mesh_color = vec3(0.0, 0.0, 0.0);
uniform bool lighting = true;
out vec4 frag_color ;
float edge_factor(){
vec3 bary3 = vec3(bary.x, bary.y, 1.0 - bary.x - bary.y);
vec3 d = fwidth(bary3);
vec3 a3 = smoothstep(vec3(0.0, 0.0, 0.0), d * mesh_width, bary3);
a3 = vec3(1.0, 1.0, 1.0) - edge_mask + edge_mask * a3;
return min(min(a3.x, a3.y), a3.z);
}
void main() {
float s = (lighting && gl_FrontFacing) ? 1.0 : -1.0;
vec4 Kdiff = gl_FrontFacing ?
gl_FrontMaterial.diffuse : gl_BackMaterial.diffuse;
float sdiffuse = s * diffuse;
vec4 result = vec4(0.1, 0.1, 0.1, 1.0);
if (sdiffuse > 0.0) {
result += sdiffuse * Kdiff +
specular * gl_FrontMaterial.specular;
}
frag_color = (mesh_width != 0.0) ?
mix(vec4(mesh_color, 1.0), result, edge_factor()) :
result;
}`
...
const uniforms = {
color: {
value: new THREE.Vector4(0, 0, 1, 1),
type: 'v4'
}
}
const material = new THREE.ShaderMaterial({
fragmentShader: data.fragmentShader,
vertexShader: data.vertexShader,
uniforms
})
this._viewer.impl.matman().addMaterial(
data.name, material, true)
const fragList = this._viewer.model.getFragmentList()
this.toArray(fragIds).forEach((fragId) => {
fragList.setMaterial(fragId, material)
})
So to implement this shader, is the right approach would be to basically check the angle between every two vertices, and draw a line if the degree is 90?
How can I have access to all the vertices of the shape from the vertex shader?
And how can I tell the fragment shader to draw a line between two vertices that match the above condition? (also to leave the default shading for everything else as is)
I'm using Autodesk viewer that uses Three.js rev 71.
// -- Vertex Shader --
precision mediump float;
// Input from buffers
attribute vec3 aPosition;
attribute vec2 aBaryCoord;
// Value interpolated accross pixels and passed to the fragment shader
varying vec2 vBaryCoord;
// Uniforms
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjMatrix;
void main() {
vBaryCoord = aBaryCoord;
gl_Position = uProjMatrix * uViewMatrix * uModelMatrix * vec4(aPosition,1.0);
}
// ---------------------
// -- Fragment Shader --
// This shader doesn't perform any lighting
precision mediump float;
varying vec2 vBaryCoord;
uniform vec3 uMeshColour;
float edgeFactor() {
vec3 d = fwidth(vBaryCoord);
vec3 a3 = smoothstep(vec3(0.0,0.0,0.0),d * 1.5,vBaryCoord);
return min(min(a3.x,a3.y),a3.z);
}
void main() {
gl_FragColor = vec4(uMeshColour,(1.0 - edgeFactor()) * 0.95);
}
// ---------------------
/*
This code isn't tested so take it with a grain of salt
Idea taken from
http://codeflow.org/entries/2012/aug/02/easy-wireframe-display-with-barycentric-coordinates/
*/

Opengl black/blank cubemap texture

I am trying to use a single cubemap texture for a skybox and reflections, but I just end up with a black texture for both. I am using a temporary array of pixels for the cubemap to make sure the image wasn't the problem. So just ignore the SDL stuff in this function; I commented some of it out. Also the texture functions and shaders are used for both 2d and cubemap textures so I commented that out as well.
Texture loading: zTexture* vars are class vars
bool loadCubeMap(std::vector<const char*> path)
{
//Texture loading success
bool textureLoaded = false;
glEnable(GL_TEXTURE_CUBE_MAP); //probably not needed
for(int j=0; j<path.size(); j++)
{
//SDL_Surface* cubFacSurf = IMG_Load(path[j]);
if(cubFacSurf != NULL)
{
//SDL_LockSurface(cubFacSurf);
zTextureW = cubFacSurf->w;
zTextureH = cubFacSurf->h;
textureLoaded = loadFromPixels((GLuint*)cubFacSurf->pixels, zTextureW, zTextureH, GL_TEXTURE_CUBE_MAP, GL_RGB, GL_TEXTURE_CUBE_MAP_POSITIVE_X+j);
//SDL_UnlockSurface(cubFacSurf);
//SDL_FreeSurface(cubFacSurf);
}
if(textureLoaded == false)
{
SDL_Log("Unable to load %s\n", path[j]);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return textureLoaded;
}
main pixel loader
bool loadFromPixels(void* pixels, GLuint width, GLuint height, const GLenum tType = GL_TEXTURE_2D, const GLenum glPix = GL_RGB, const GLenum tFace = GL_TEXTURE_2D)
{
glGenTextures(1, &zTextureID);
glBindTexture(tType, zTextureID);
GLfloat checkerboard[] = {1.f,1.f,1.f, 0.f,0.f,0.f, 0.f,0.f,0.f, 1.f,0.f,1.f};
if(tType == GL_TEXTURE_CUBE_MAP)
glTexImage2D(tFace, 0, glPix, 2, 2, 0, glPix, GL_FLOAT, &checkerboard);
//else
//glTexImage2D(tFace, 0, glPix, width, height, 0, glPix, GL_UNSIGNED_BYTE, pixels);
//Check for error
GLenum error = glGetError();
if( error != GL_NO_ERROR )
{
SDL_Log( "Error loading texture from %p pixels! %d\n", pixels, error);
return false;
}
return true;
}
Texture Binding:
void apply(const GLenum typ = GL_TEXTURE_2D)
{
glEnable(typ); //Probably not necessary doesnt change anything if left out
if(typ == GL_TEXTURE_2D)
{
glDisable(GL_TEXTURE_CUBE_MAP); //same here
glUniform1i(mainTxtrID, 0); //mainTxtrID = Textr in frag
glActiveTexture(GL_TEXTURE0);
}
else
{
glDisable(GL_TEXTURE_2D); //and here
glUniform1i(cubeID, 1); //cubeID = TextCub in frag
glActiveTexture(GL_TEXTURE1);
}
glBindTexture(typ, zTextureID);
}
"Uber" Shaders:
vertex:
#version 100
precision mediump float;
uniform mat4 ModelMat;
uniform mat4 ViewMat;
uniform mat4 ProjMat;
uniform mat4 OrthMat;
uniform bool world;
attribute vec4 vPosition;
attribute vec2 UVCoordAt;
attribute vec3 nPosition;
varying vec2 UVCoord;
varying vec3 vPos;
varying vec3 vNor;
varying vec3 vRefl;
void main()
{
UVCoord = UVCoordAt;
vPos = vec3(vPosition); //skybox coords
vNor = normalize(vec3(ModelMat * vec4(nPosition,0.0)));
vRefl = reflect(normalize(vPos - vec3(ViewMat[3][0], ViewMat[3][1], ViewMat[3][2])), vNor); //reflection direction vector
if(world)
gl_Position = ProjMat * ViewMat * ModelMat * vPosition;
else gl_Position = OrthMat * ModelMat * vPosition;
}
fragment:
#version 100
precision mediump float;
uniform samplerCube TextCub;
uniform sampler2D Textr;
uniform vec3 LiPos;
uniform vec4 fragCol;
uniform bool lighting;
uniform bool dimen;
uniform bool isRefl;
varying vec2 UVCoord;
varying vec3 vPos;
varying vec3 vNor;
varying vec3 vRefl;
void main()
{
vec4 textVect = texture2D(Textr, UVCoord); //default texturing
if(dimen){ textVect = textureCube(TextCub, vPos); } //skybox
else if(isRefl){ textVect = mix(textVect, textureCube(TextCub, vRefl), 0.7); } //reflections mixed with default textr
if(lighting){
float diffuse = clamp(dot(vNor, LiPos), 0.0, 1.0);
gl_FragColor = clamp(diffuse*2.0, 0.6, 1.1) * fragCol * textVect;
}
else{ gl_FragColor = fragCol * textVect; }
}
I am using GL_DEPTH_TEST, I doubt this affects anything. I am guessing the problem is in the apply() function or something else I left out. There are extensions for cubemaps but I assume the default opengles 2 cubemaps work without them.
You're creating a new texture for each cubemap face. In the loadFromPixels() function, which you call for each face:
glGenTextures(1, &zTextureID);
glBindTexture(tType, zTextureID);
...
glTexImage2D(...);
This means that you will end up with 6 textures that each have only data for one face specified, which makes them incomplete.
You need to create one cubemap texture, and then specify data for all 6 sides of that cubemap.

glsl bind computed fragment shader to cube face

I have a cube , i want to bind a computed fragment shader to a cube face. any help?
my vertex shader is :
precision highp float;
attribute vec3 position;
attribute vec3 normal;
uniform mat3 normalMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
varying vec3 fNormal;
varying vec3 fPosition;
void main()
{
fNormal = normalize(normalMatrix * normal);
vec4 pos = modelViewMatrix * vec4(position, 1.0);
fPosition = pos.xyz;
gl_Position = projectionMatrix * pos;
}
and my fragment shader:
precision highp float;
uniform float time;
uniform vec2 resolution;
varying vec3 fPosition;
varying vec3 fNormal;
void main()
{
float minDimension = min(resolution.x, resolution.y);
vec2 bounds = vec2(resolution.x / minDimension, resolution.y / minDimension);
vec2 uv = gl_FragCoord.xy / minDimension;
vec2 midUV = vec2(bounds.x * 0.5, bounds.y * 0.5);
uv.xy-=midUV;
float dist = sqrt(dot(uv, uv));
float t = smoothstep(0.2+0.1*cos(time), 0.1-0.01, dist);
vec4 disc_color=vec4(1.0,0.,0.,1.);
vec4 bkg_color=vec4(0.0, 0.0, 0.0, 1.0);
vec4 resultColor;
resultColor = mix(bkg_color, disc_color, t);
gl_FragColor = resultColor;
}
i get a cube with a dot on the hole image.
but what i want is actually to have the dot on each face.
you can test the code directly under http://shdr.bkcore.com/
The standard approach to do this in OpenGL is to first render your content to a FBO (Frame Buffer Object). This allows you to render to a texture. You can then use this texture for one of the sides when rendering the cube.
The following is not complete code, but just an outline to give you a starting point:
// create a texture, and bind it
GLuint texId = 0;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
// set texture parameters (filters, wrap mode, etc)
glTexParameteri(GL_TEXTURE_2D, ...);
// allocate texture data (no data provided)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, texId);
// create and set up FBO with texture as attachment
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
// create a depth renderbuffer and attach it if depth testing is needed
// start rendering
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
glDraw...(...);
// back to default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, windowWidth, windowHeight);
glClear(GL_COLOR_BUFFER_BIT);
// render cube using texture we rendered to
glBindTexture(GL_TEXTURE_2D, texId);
// rest of state setup and rendering
thx for the answer, this is right, but i found a better way then rendering to a texture. i passed the texture coordinates and used them instead of the glfragcoord in the fragment shader.

Resources