1. Add the texture image file
- Download this image.
- Open the Project pane from the left side of the IDE.
- Select the Project view.
- Navigate to app->src->main.
- Right click the main folder and select New->Directory.
- Name it assets and click OK.
- Drag and drop the image to the newly created assets directory and click OK.
- Return to the Android view.
2. Extract the texture image file
- Open the MainActivity.kt file.
- Add the following function:
private fun extractFiles() { val fileNames = arrayOf("texture-wood.jpg") val filesDir = filesDir.absolutePath val assetManager = assets for (fileName in fileNames) { val file = File(filesDir, fileName) if (!file.exists()) { val bytes = assetManager.open(fileName).readBytes() file.writeBytes(bytes) } } }
- You will also need to add the following import statement:
import java.io.File
- Modify the onCreate function as follows:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) customGLSurfaceView = findViewById(R.id.customGLSurfaceView) extractFiles() }
3. Add the image loading library
- Download this file.
- Open the Project pane from the left side of the IDE.
- Select the Project view.
- Navigate to app->src->main.
- Drag and drop the stb_image.h file onto the cpp folder and click OK.
- Return to the Android view.
4. Load the texture image
- Open the main.cpp file.
- Add the following code at the top of the file:
#define STB_IMAGE_IMPLEMENTATION #import "stb_image.h" #include <string> #include <jni.h> #include <GLES3/gl31.h> /* code */
- Add the following global variables:
/* code */ glm::mat4 projectionMatrix; unsigned char *texture_image_data; int texture_image_width; int texture_image_height; int texture_image_depth; float currentAngle = 0.0f; /* code */
- Add the following function:
extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_loadTextureImageFile(JNIEnv * env, jobject obj, jstring aFilesDir) { const char *files_dir_UTF_chars = env->GetStringUTFChars(aFilesDir, NULL); std::string files_dir(files_dir_UTF_chars); env->ReleaseStringUTFChars(aFilesDir, files_dir_UTF_chars); std::string texture_image_file_path = files_dir + "/texture-wood.jpg"; texture_image_data = stbi_load(texture_image_file_path.c_str(), &texture_image_width, &texture_image_height, &texture_image_depth, 0); if (!texture_image_data) { LOGE("Failed to load texture image file: %s", texture_image_file_path.c_str()); } return; }
- Open the NativeLibrary.kt file.
- Add the following lines of code:
/* code */ external fun nOnSurfaceChanged(width: Int, height: Int) external fun nOnDrawFrame() external fun loadTextureImageFile(aFilesDir: String) /* code */
- Open the MainActivity.kt file.
- Add the following property:
/* code */ class MainActivity: AppCompatActivity() { private val nativeLibrary = NativeLibrary() private lateinit var customGLSurfaceView: CustomGLSurfaceView /* code */ }
- Modify the onCreate function as follows:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) customGLSurfaceView = findViewById(R.id.customGLSurfaceView) extractFiles() nativeLibrary.loadTextureImageFile(filesDir.absolutePath) }
5. Create the texture
- Open the main.cpp file.
- Declare the following global variable:
/* code */ GLuint program, triangleVAO, triangleVBO, triangleIBO, texture; GLint uniformModel, uniformProjection, uniformView; glm::mat4 projectionMatrix; /* code */
- Modify the Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnSurfaceCreated function as follows:
extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnSurfaceCreated(JNIEnv * env, jobject obj) { glClearColor(0.0, 1.0, 0.0, 1.0); glEnable(GL_DEPTH_TEST); createProgram(); createTriangle(); glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texture_image_width, texture_image_height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture_image_data); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(texture_image_data); return; }
6. Add the texture coordinates
- Modify the createTriangle function as follows:
void createTriangle() { /* code */ GLfloat vertices[] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 1.0f, 0.5f, 0.0f, 1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, -1.0f, -1.0f, 0.5f, 0.5f }; /* code */ glGenBuffers(1, &triangleVBO); glBindBuffer(GL_ARRAY_BUFFER, triangleVBO); glBufferData(GL_ARRAY_BUFFER, 25 * sizeof(GL_FLOAT), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 5, 0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 5, (void*)(sizeof(GL_FLOAT) * 3)); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); /* code */ }
7. Draw the texture
7.1. Modify the vertex shader
- Modify the vertex shader source code as follows:
static const GLchar vertexShaderSource[] = "#version 310 es\n" "layout (location = 0) in vec3 pos;\n" "layout (location = 1) in vec2 texCoords;\n" "out vec4 vColor;\n" "out vec2 vTexCoords;\n" "uniform mat4 model;\n" "uniform mat4 projection;\n" "uniform mat4 view;\n" "void main()\n" "{\n" "gl_Position = projection * view * model * vec4(pos, 1.0);\n" "vColor = vec4(clamp(pos, 0.0, 1.0), 1.0);\n" "vTexCoords = texCoords;\n" "}\n";
7.2 Modify the fragment shader
- Modify the fragment shader source code:
static const GLchar fragmentShaderSource[] = "#version 310 es\n" "precision mediump float;\n" "in vec4 vColor;\n" "in vec2 vTexCoords;\n" "out vec4 color;\n" "uniform sampler2D textureSampler;\n" "void main()\n" "{\n" "color = texture(textureSampler, vTexCoords);\n" "}\n";
Info
You don’t need to set a value to the uniform variable because it already has a default value of 0. This means that the sampler will work with the GL_TEXTURE0 texture unit.
If for example you intend to use the GL_TEXTURE1 texture unit then you should set the uniform variable to 1.
7.3 Activate the texture
- Modify the Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnDrawFrame function as follows:
extern "C" JNIEXPORT void JNICALL Java_dev_anastasioscho_glestriangle_NativeLibrary_nOnDrawFrame(JNIEnv * env, jobject obj) { /* code */ glm::mat4 viewMatrix = glm::lookAt(cameraPosition, cameraTarget, worldUp); glUniformMatrix4fv(uniformView, 1, GL_FALSE, glm::value_ptr(viewMatrix)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); /* code */ return; }
Leave a Reply