I have a very strange issue I'm struggling with regarding using GLSurfaceView with react native on Android.
I've created a custom OpenGL renderer that I want to use as a component in a larger react native view. The problem is that when I size the component on the JS side to the size I want the rest of the screen goes black.

This is the React Native code that's creating that view:
public render() {
return (
<View style={{ backgroundColor: '#ffffff' }}>
<Text>Hello World!</Text>
<ImageBlendView style={{ height: 50 }} boxColour="#f442df" />
</View>
);
}
ImageBlendView is a react-native UI component I made following the instructions on the RN website here, this is what's creating the red with the purple rectangle in it. It is a just a pretty basic Opengl renderer that is drawing a rectangle with the color passed in from JS and a red clear color. I added a text field above it and sized the component to 50-height.
I can't figure out why the ImageBlendView component is surrounded by black.
The clear color is red so I know its not my renderer clearing pixels it shouldn't be. However, it does seem to be related to my renderer class because when I comment out the line in the GLSurfaceView class where I set it as the renderer the normal white background comes back and the text above the component is visible.
// setRenderer(new OpenGLRenderer(_context, colour));
The custom UI component follows the same structure as in the tutorial but I'll walk through it to be thorough.
The ImageBlendView is added to a package and bundled in the view managers list:
public class OTSNativePackage implements ReactPackage {
@Nonnull
@Override
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
List<NativeModule> nativeModules = new ArrayList<>();
return nativeModules;
}
@Nonnull
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(new ImageBlendView());
}
}
ImageBlendView.java then extends the SimpleViewManager which creates an instance of OpenGLView which extends GLSurfaceView
public class ImageBlendView extends SimpleViewManager<OpenGLView> {
public static final String REACT_CLASS = "ImageBlendView";
private static OpenGLView _openGLView;
@Nonnull
@Override
public String getName() {
return REACT_CLASS;
}
@Nonnull
@Override
protected OpenGLView createViewInstance(@Nonnull ThemedReactContext reactContext) {
_openGLView = new OpenGLView(reactContext);
return _openGLView;
}
@ReactProp(name = "boxColour")
public void setBoxColour(OpenGLView view, String color) {
_openGLView.init(color);
}
}
OpenGLView.java
public class OpenGLView extends GLSurfaceView {
private Context _context;
public OpenGLView(Context context) {
super(context);
_context = context;
}
public OpenGLView(Context context, AttributeSet attrs) {
super(context, attrs);
_context = context;
}
public void init(String colour){
setEGLContextClientVersion(2);
setPreserveEGLContextOnPause(true);
// If I disable the following line the black goes away
setRenderer(new OpenGLRenderer(_context, colour));
}
}
Note that it is here in the above class where I can disable the setRenderer line and the black goes away.
My Renderer is in OpenGLRenderer.java
public class OpenGLRenderer implements GLSurfaceView.Renderer {
private Context _context;
private FloatBuffer vertexBuffer;
private ShortBuffer drawListBuffer;
private int positionHandle, program, colorHandle;
static final int COORDS_PER_VERTEX = 3;
static float squareCoords[] = {
-0.5f, 0.5f, 0.0f, // top left
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f, // bottom right
0.5f, 0.5f, 0.0f }; // top right
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
float color[] = {0.5f, 0.5f, 0.55f, 1.0f};
private int vertexCount;
private int vertexStride; // 4 bytes per vertex
public OpenGLRenderer(Context context, String colorStr){
super();
_context = context;
color = new float[]{(float)Integer.valueOf( colorStr.substring( 1, 3 ), 16 ) / 256.0f,
(float)Integer.valueOf( colorStr.substring( 3, 5 ), 16 ) / 256.0f,
(float)Integer.valueOf( colorStr.substring( 5, 7 ), 16 ) / 256.0f, 1.0f};
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(1f,0f,0f,1f);
ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
vertexCount = squareCoords.length / COORDS_PER_VERTEX;
vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
compileShaders();
sendData();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
GLES20.glScissor(0,0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
GLES20.glDisableVertexAttribArray(positionHandle);
}
private void sendData(){
positionHandle = GLES20.glGetAttribLocation(program, "position");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
colorHandle = GLES20.glGetUniformLocation(program, "vColor");
GLES20.glUniform4fv(colorHandle, 1, color, 0);
}
private void compileShaders() {
int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
GLES20.glShaderSource(vertexShader, getShaderSource(R.raw.vertex_shader));
int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fragmentShader, getShaderSource(R.raw.fragment_shader));
GLES20.glCompileShader(vertexShader);
GLES20.glCompileShader(fragmentShader);
program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
GLES20.glUseProgram(program);
}
private String getShaderSource(int input){
StringBuilder total = new StringBuilder();
try{
InputStream stream =
_context.getResources().openRawResource(input);
BufferedReader r = new BufferedReader(new InputStreamReader(stream));
total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
} catch (IOException e){
System.out.println(e.getMessage());
}
return total.toString();
}
}
The latest thing I tried was the call to GLES20.glScissor(0,0, width, height); in the onSurfaceChanged method. I was hoping by scissoring to just the size and height of the component it wouldn't affect the rest of the pixels but its becoming pretty clear that its not an issue with the OpenGL but the setup.
Finally figured out a solution. I simply changed the JS code to this:
public render() {
console.log('***', ImageBlend);
return (
<View style={{ backgroundColor: '#ffffff' }}>
<Text>Hello Nick!</Text>
<View style={{ height: 50, overflow: 'hidden' }}>
<ImageBlendView
style={{ height: 50 }}
boxColour="#f442df"
/>
</View>
</View>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With