This epic work appeared thanks to several events.
class Light3D { public float[] pos; public float[] color; } class Material3D { public float[] ambient; public float[] diffuse; } class FaceMat { public Material3D material; public int faces; public short[] indexBuffer; public int bufOffset; } class Object3D { public ArrayList<FaceMat> faceMats; public int vertCount; public int indCount; public int glVertices; public int glIndices; public float[] vertexBuffer; } public class Scene3D { public ArrayList<Material3D> materials; public ArrayList<Object3D> objects; public ArrayList<Light3D> lights; public float[] ambient; } int[] genbuf = new int[1]; private int createBuffer(float[] buffer) { FloatBuffer floatBuf = ByteBuffer.allocateDirect(buffer.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); floatBuf.put(buffer); floatBuf.position(0); GLES20.glGenBuffers(1, genbuf, 0); int glBuf = genbuf[0]; GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, glBuf); GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.length * 4, floatBuf, GLES20.GL_STATIC_DRAW); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); return glBuf; } ... int i, num = scene.objects.size(); for (i = 0; i < num; i++) { Object3D obj = scene.objects.get(i); obj.glVertices = createBuffer(obj.vertexBuffer); GLES20.glGenBuffers(1, genbuf, 0); obj.glIndices = genbuf[0]; GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, obj.glIndices); GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, obj.indCount * 2, null, GLES20.GL_STATIC_DRAW); int k, mats = obj.faceMats.size(); for (k = 0; k < mats; k++) { FaceMat mat = obj.faceMats.get(k); ShortBuffer indBuf = ByteBuffer.allocateDirect(mat.indexBuffer.length * 2).order(ByteOrder.nativeOrder()).asShortBuffer(); indBuf.put(mat.indexBuffer); indBuf.position(0); GLES20.glBufferSubData(GLES20.GL_ELEMENT_ARRAY_BUFFER, mat.bufOffset * 2, mat.indexBuffer.length * 2, indBuf); } GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); } private final String vertexShaderCode = "precision mediump float;\n" + "uniform mat4 uMVPMatrix;\n" + "uniform mat4 uMVMatrix;\n" + "uniform mat3 uNMatrix;\n" + "uniform vec4 uAmbient;\n" + "uniform vec4 uDiffuse;\n" + "const int MaxLights = 8;\n" + "struct LightSourceParameters {\n" + " bool enabled;\n" + " vec4 color;\n" + " vec3 position;\n" + "};\n" + "uniform LightSourceParameters uLight[MaxLights];\n" + "attribute vec4 vPosition;\n" + "attribute vec3 vNormal;\n" + "varying vec4 FrontColor;\n" + "vec4 light_point_view_local(vec3 epos, vec3 normal, int idx);\n" + "void main() {\n" + " gl_Position = uMVPMatrix * vPosition;\n" + " vec4 epos = uMVMatrix * vPosition;\n" + " vec3 normal =uNMatrix * vNormal;\n" + " vec4 vcolor = uAmbient;\n" + " int i;\n" + " for (i = 0; i < MaxLights; i++) {\n" + " if (uLight[i].enabled) {\n" + " vcolor += light_point_view_local(epos.xyz, normal, i);\n" + " }\n" + " }\n" + " FrontColor = clamp(vcolor, 0.0, 1.0);\n" + "}\n" + "vec4 light_point_view_local(vec3 epos, vec3 normal, int idx) {\n" + " vec3 vert2light = uLight[idx].position - epos;\n" + " vec3 ldir = normalize(vert2light);\n" + " float NdotL = dot(normal, ldir);\n" + " vec4 outCol = vec4(0.0, 0.0, 0.0, 1.0);\n" + " if (NdotL > 0.0) {\n" + " outCol = uLight[idx].color * uDiffuse * NdotL;\n" + " }\n" + " return outCol;\n" + "}\n"; private final String fragmentShaderCode = "precision mediump float;\n" + "varying vec4 FrontColor;\n" + "void main() {\n" + " gl_FragColor = FrontColor;\n" + "}\n"; private int mProgram; private int maPosition; private int maNormal; private int muMVPMatrix; private int muMVMatrix; private int muNMatrix; private int muAmbient; private int muDiffuse; private int[] muLightOn = new int[8]; private int[] muLightPos = new int[8]; private int[] muLightCol = new int[8]; private int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); Log.i("Shader", GLES20.glGetShaderInfoLog(shader)); return shader; } private int Compile(String vs, String fs) { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vs); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fs); int prog = GLES20.glCreateProgram(); // create empty OpenGL Program GLES20.glAttachShader(prog, vertexShader); // add the vertex shader to program GLES20.glAttachShader(prog, fragmentShader); // add the fragment shader to program GLES20.glLinkProgram(prog); // creates OpenGL program executables return prog; } ... mProgram = Compile(vertexShaderCode, fragmentShaderCode); // get handle to the vertex shader's vPosition member maPosition = GLES20.glGetAttribLocation(mProgram, "vPosition"); maNormal = GLES20.glGetAttribLocation(mProgram, "vNormal"); muMVPMatrix = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); muMVMatrix = GLES20.glGetUniformLocation(mProgram, "uMVMatrix"); muNMatrix = GLES20.glGetUniformLocation(mProgram, "uNMatrix"); muAmbient = GLES20.glGetUniformLocation(mProgram, "uAmbient"); muDiffuse = GLES20.glGetUniformLocation(mProgram, "uDiffuse"); int i; for (i = 0; i < 8; i++) { muLightOn[i] = GLES20.glGetUniformLocation(mProgram, String.format("uLight[%d].enabled", i)); muLightPos[i] = GLES20.glGetUniformLocation(mProgram, String.format("uLight[%d].position", i)); muLightCol[i] = GLES20.glGetUniformLocation(mProgram, String.format("uLight[%d].color", i)); } private void DrawScene() { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glUseProgram(mProgram); GLES20.glEnable(GLES20.GL_CULL_FACE); GLES20.glEnable(GLES20.GL_DEPTH_TEST); Matrix.multiplyMM(mMVMatrix, 0, mVMatrix, 0, mMMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVMatrix, 0); // Apply a ModelView Projection transformation GLES20.glUniformMatrix4fv(muMVPMatrix, 1, false, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVMatrix, 1, false, mMVMatrix, 0); int i, j, num; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) mNMatrix[i*3 + j] = mMVMatrix[i*4 + j]; GLES20.glUniformMatrix3fv(muNMatrix, 1, false, mNMatrix, 0); num = min(scene.lights.size(), 8); float[] eyepos = new float[3]; for (i = 0; i < num; i++) { Light3D light = scene.lights.get(i); for (j = 0; j < 3; j++) { eyepos[j] = mVMatrix[4*3 + j]; for (k = 0; k < 3; k++) eyepos[j] += light.pos[k] * mVMatrix[k*4 + j]; } GLES20.glUniform1i(muLightOn[i], 1); GLES20.glUniform3fv(muLightPos[i], 1, eyepos, 0); GLES20.glUniform4fv(muLightCol[i], 1, light.color, 0); } for (i = num; i < 8; i++) GLES20.glUniform1i(muLightOn[i], 0); // Prepare the triangle data GLES20.glEnableVertexAttribArray(maPosition); GLES20.glEnableVertexAttribArray(maNormal); num = scene.objects.size(); for (i = 0; i < num; i++) { Object3D obj = scene.objects.get(i); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, obj.glVertices); GLES20.glVertexAttribPointer(maPosition, 3, GLES20.GL_FLOAT, false, 24, 0); GLES20.glVertexAttribPointer(maNormal, 3, GLES20.GL_FLOAT, false, 24, 12); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, obj.glIndices); int mats = obj.faceMats.size(); for (j = 0; j < mats; j++) { FaceMat mat = obj.faceMats.get(j); for (int k = 0; k < 3; k++) mAmbient[k] = mat.material.ambient[k] * scene.ambient[k]; GLES20.glUniform4fv(muAmbient, 1, mAmbient, 0); GLES20.glUniform4fv(muDiffuse, 1, mat.material.diffuse, 0); GLES20.glDrawElements(GLES20.GL_TRIANGLES, mat.indexBuffer.length, GLES20.GL_UNSIGNED_SHORT, mat.bufOffset * 2); } GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); } GLES20.glDisableVertexAttribArray(maPosition); GLES20.glDisableVertexAttribArray(maNormal); } 
private final String quadVS = "precision mediump float;\n" + "attribute vec4 vPosition;\n" + "attribute vec4 vTexCoord0;\n" + "varying vec4 TexCoord0;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + " TexCoord0 = vTexCoord0;\n" + "}\n"; private final String quadFS = "precision mediump float;\n" + "uniform sampler2D uTexture0;\n" + "varying vec4 TexCoord0;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture0, TexCoord0.xy);\n" + "}\n"; private int mQProgram; private int maQPosition; private int maQTexCoord; private int muQTexture; private int glQuadVB; ... final float quadv[] = { -1, 1, 0, 0, 1, -1, -1, 0, 0, 0, 1, 1, 0, 1, 1, 1, -1, 0, 1, 0 }; glQuadVB = createBuffer(quadv); mQProgram = Compile(quadVS, quadFS); maQPosition = GLES20.glGetAttribLocation(mQProgram, "vPosition"); maQTexCoord = GLES20.glGetAttribLocation(mQProgram, "vTexCoord0"); muQTexture = GLES20.glGetUniformLocation(mQProgram, "uTexture0"); private int filterBuf1; private int filterBuf2; private int renderTex1; private int renderTex2; public int scrWidth; public int scrHeight; public int texWidth; public int texHeight; ... private int makeRenderTarget(int width, int height, int[] handles) { GLES20.glGenTextures(1, genbuf, 0); int renderTex = genbuf[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); IntBuffer texBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder()).asIntBuffer(); GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, texBuffer); GLES20.glGenRenderbuffers(1, genbuf, 0); int depthBuf = genbuf[0]; GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthBuf); GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height); GLES20.glGenFramebuffers(1, genbuf, 0); int frameBuf = genbuf[0]; GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuf); GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, renderTex, 0); GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthBuf); int res = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); handles[0] = frameBuf; handles[1] = renderTex; return res; } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { ratio = (float) width / height; int[] handles = new int[2]; scrWidth = width; scrHeight = height; texWidth = 256; texHeight = 256; makeRenderTarget(texWidth, texHeight, handles); filterBuf1 = handles[0]; renderTex1 = handles[1]; makeRenderTarget(texWidth, texHeight, handles); filterBuf2 = handles[0]; renderTex2 = handles[1]; } private void setRenderTexture(int frameBuf, int texture) { if (frameBuf == 0) GLES20.glViewport(0, 0, scrWidth, scrHeight); else GLES20.glViewport(0, 0, texWidth, texHeight); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuf); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture); } private void DrawQuad() { GLES20.glUseProgram(mQProgram); GLES20.glDisable(GLES20.GL_DEPTH_TEST); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, glQuadVB); GLES20.glEnableVertexAttribArray(maQPosition); GLES20.glVertexAttribPointer(maQPosition, 3, GLES20.GL_FLOAT, false, 20, 0); GLES20.glEnableVertexAttribArray(maQTexCoord); GLES20.glVertexAttribPointer(maQTexCoord, 2, GLES20.GL_FLOAT, false, 20, 12); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); GLES20.glUniform1i(muQTexture, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDisableVertexAttribArray(maQPosition); GLES20.glDisableVertexAttribArray(maQTexCoord); }
Since the effect will overlap in several passes, for performance reasons, the texture is low resolution, so the result of its rendering on the quad will be similar to something like the one shown in the picture. class FilterKernelElement { public float du; public float dv; public float coef; } ... float mOffsets[] = new float[4]; private float[] pix_mult = new float[4]; private FilterKernelElement[] mvGaussian1D = new FilterKernelElement[44]; private float mfPerTexelWidth; private float mfPerTexelHeight; ... float cent = (mvGaussian1D.length - 1.0f) / 2.0f, radi; for (int u = 0; u < mvGaussian1D.length; u++) { FilterKernelElement el = mvGaussian1D[u] = new FilterKernelElement(); el.du = ((float)u) - cent - 0.1f; el.dv = 0.0f; radi = (el.du * el.du) / (cent * cent); el.coef = (float)((0.24/Math.exp(radi*0.18)) + 0.41/Math.exp(radi*4.5)); } float rr = texWidth / (float) texHeight; float rs = rr / ratio; mfPerTexelWidth = rs / texWidth; mfPerTexelHeight = 1.0f / texHeight; private final String gaussVS = "precision mediump float;\n" + "attribute vec4 vPosition;\n" + "attribute vec4 vTexCoord0;\n" + "uniform vec4 uTexOffset0;\n" + "uniform vec4 uTexOffset1;\n" + "uniform vec4 uTexOffset2;\n" + "uniform vec4 uTexOffset3;\n" + "varying vec4 TexCoord0;\n" + "varying vec4 TexCoord1;\n" + "varying vec4 TexCoord2;\n" + "varying vec4 TexCoord3;\n" + "void main() {\n" + " gl_Position = vPosition;\n" + " TexCoord0 = vTexCoord0 + uTexOffset0;\n" + " TexCoord1 = vTexCoord0 + uTexOffset1;\n" + " TexCoord2 = vTexCoord0 + uTexOffset2;\n" + " TexCoord3 = vTexCoord0 + uTexOffset3;\n" + "}\n"; private final String gaussFS = "precision mediump float;\n" + "uniform sampler2D uTexture0;\n" + "uniform vec4 uTexCoef0;\n" + "uniform vec4 uTexCoef1;\n" + "uniform vec4 uTexCoef2;\n" + "uniform vec4 uTexCoef3;\n" + "varying vec4 TexCoord0;\n" + "varying vec4 TexCoord1;\n" + "varying vec4 TexCoord2;\n" + "varying vec4 TexCoord3;\n" + "void main() {\n" + " vec4 c0 = texture2D(uTexture0, TexCoord0.xy);\n" + " vec4 c1 = texture2D(uTexture0, TexCoord1.xy);\n" + " vec4 c2 = texture2D(uTexture0, TexCoord2.xy);\n" + " vec4 c3 = texture2D(uTexture0, TexCoord3.xy);\n" + " gl_FragColor = uTexCoef0 * c0 + uTexCoef1 * c1 + uTexCoef2 * c2 + uTexCoef3 * c3;\n" + "}\n"; private int mGProgram; private int maGPosition; private int maGTexCoord; private int muGTexture; private int[] muGTexCoef = new int[4]; private int[] muGTexOffset = new int[4]; ... mGProgram = Compile(gaussVS, gaussFS); maGPosition = GLES20.glGetAttribLocation(mGProgram, "vPosition"); maGTexCoord = GLES20.glGetAttribLocation(mGProgram, "vTexCoord0"); muGTexture = GLES20.glGetUniformLocation(mGProgram, "uTexture0"); for (i = 0; i < 4; i++) { muGTexOffset[i] = GLES20.glGetUniformLocation(mGProgram, String.format("uTexOffset%d", i)); muGTexCoef[i] = GLES20.glGetUniformLocation(mGProgram, String.format("uTexCoef%d", i)); } private void DrawGauss(boolean invert) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mGProgram); GLES20.glDisable(GLES20.GL_DEPTH_TEST); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, glQuadVB); GLES20.glEnableVertexAttribArray(maGPosition); GLES20.glVertexAttribPointer(maGPosition, 3, GLES20.GL_FLOAT, false, 20, 0); GLES20.glEnableVertexAttribArray(maGTexCoord); GLES20.glVertexAttribPointer(maGTexCoord, 2, GLES20.GL_FLOAT, false, 20, 12); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); GLES20.glUniform1i(muGTexture, 0); int i, n, k; for (i = 0; i < mvGaussian1D.length; i += 4) { for (n = 0; n < 4; n++) { FilterKernelElement pE = mvGaussian1D[i + n]; for (k = 0; k < 4; k++) pix_mult[k] = pE.coef * 0.10f; GLES20.glUniform4fv(muGTexCoef[n], 1, pix_mult, 0); mOffsets[0] = mfPerTexelWidth * (invert ? pE.dv : pE.du); mOffsets[1] = mfPerTexelHeight * (invert ? pE.du : pE.dv); GLES20.glUniform4fv(muGTexOffset[n], 1, mOffsets, 0); } GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } GLES20.glDisableVertexAttribArray(maGPosition); GLES20.glDisableVertexAttribArray(maGTexCoord); } 

@Override public void onDrawFrame(GL10 arg0) { setRenderTexture(filterBuf1, 0); DrawScene(); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE); setRenderTexture(filterBuf2, renderTex1); DrawGauss(false); setRenderTexture(filterBuf1, renderTex2); DrawGauss(true); GLES20.glDisable(GLES20.GL_BLEND); setRenderTexture(0, 0); DrawScene(); GLES20.glEnable(GLES20.GL_BLEND); GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE); setRenderTexture(0, renderTex1); DrawQuad(); GLES20.glDisable(GLES20.GL_BLEND); } for (i = 0; i < uLights; i++) Source: https://habr.com/ru/post/144831/
All Articles