ImportantDue to NaCl deprecation by the Chromium project, Tizen TV will continue its support for NaCl only until 2021-year products. Meanwhile, Tizen TV will start focusing on high-performance, cross-browser WebAssembly from 2020-year products.
This topic describes the "3D Graphics in C++" sample application implementation.
This tutorial describes how to use OpenGL® ES 2.0 to implement an interactive 3D animation in C++ as a Native Client (NaCl) embed object. The sample application renders a rotating, textured cube, whose rotation can be controlled by clicking and dragging the mouse.
When the cube is clicked, automatic cube rotation stops. The user can drag the cube to rotate it; mouse movement is correlated to changes in the rotation around the X and Z axes, indirectly changing the rotation matrix values. Clicking the cube again resumes automatic rotation.
For information on how to access the sample application cheat sheet and run the application, see Sample-based Tutorials.
Initializing the Instance and Context
To implement 3D graphics with OpenGL® ES 2.0, you must initialize the instance, the rendering context, and the rendering pipeline:
In the class constructor, initialize the class members:
callback_factory_ simplifies the creation of completion callbacks used to implement the main drawing loop.
context_ is a pp::Graphics3D object.
width_, height_, and scale_ define the size and scale of the viewport.
Various OpenGL handles, such as for shaders, program, and attribute locations.
Mouse state variables store the mouse pointer location and button state.
x_angle_ and y_angle_ describe the current rotation state of the rendered cube.
Enable mouse input events and initialize the Loggerclass:
The DidChangeView() function is called whenever a viewport is created or changed, and it provides information about the viewport, such as its size and location.
In the DidChangeView() function, retrieve the device scale and size, initialize OpenGL if necessary, and create the rendering context. Compile and link the shaders to the graphic program, initialize the buffers, and start the rendering loop. If the context was already initialized, resize the buffers to fit the viewport.
void Graphics3DCubeInstance::DidChangeView(const pp::View& view) {
// Pepper specifies dimensions in DIPs (device-independent pixels)
// To generate a context that is at device-pixel resolution on HiDPI devices,
// scale the dimensions using view.GetDeviceScale()
device_scale_ = view.GetDeviceScale();
int32_t new_width = view.GetRect().width() * device_scale_;
int32_t new_height = view.GetRect().height() * device_scale_;
if (context_.is_null()) {
if (!InitGL(new_width, new_height)) {
Logger::Error("Couldn't initialize GLES library!");
return;
}
// Prepare the pipeline and start drawing
InitShaders();
InitBuffers();
InitTexture();
MainLoopIteration(0);
} else {
// Resize the buffers to the new size of the module
int32_t result = context_.ResizeBuffers(new_width, new_height);
if (result < 0) {
Logger::Error("Unable to resize buffers to %d x %d!", new_width,
new_height);
return;
}
}
width_ = new_width;
height_ = new_height;
glViewport(0, 0, width_, height_);
Logger::Log("Initialized module's view with resolution %dx%d",
width_, height_);
}
In the InitGL() function, create the pp::Graphics3D rendering context:
Initialize the OpenGL® ES library using the glInitializePPAPI() function.
Create the attrib_list array with the viewport properties, such as the alpha size (in bits), depth buffer size, and viewport dimensions.
Pass the viewport properties to the pp:Graphics3D context constructor.
Bind the graphics context to the application viewport using the BindGraphics() function. If viewport binding is successful, the new graphics context is set to active and the InitGL() function returns true.
bool Graphics3DCubeInstance::InitGL(int32_t new_width, int32_t new_height) {
// Initialize the OpenGL ES library and its connection with this NaCl module
// This must be called once before making any GL calls
// For more information, see ppapi/lib/gl/gles2/gl2ext_ppapi.h
if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface())) {
Logger::Error("Unable to initialize GLES PPAPI!");
return false;
}
const int32_t attrib_list[] = {
PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24,
PP_GRAPHICS3DATTRIB_WIDTH, new_width,
PP_GRAPHICS3DATTRIB_HEIGHT, new_height,
PP_GRAPHICS3DATTRIB_NONE
};
// Create a 3D graphics context and bind it to this instance
context_ = pp::Graphics3D(this, attrib_list);
if (!BindGraphics(context_)) {
Logger::Error("Unable to bind 3D context!");
context_ = pp::Graphics3D();
glSetCurrentContextPPAPI(0);
return false;
}
// Set the rendering context as current
glSetCurrentContextPPAPI(context_.pp_resource());
Logger::Log("Initialized GLES library.");
return true;
}
In the InitShaders() function, compile the fragment and vertex shaders, link them to the graphics program, and save the attribute and uniform locations:
void Graphics3DCubeInstance::InitShaders() {
frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, kFragShaderSource);
if (!frag_shader_)
return;
vertex_shader_ = CompileShader(GL_VERTEX_SHADER, kVertexShaderSource);
if (!vertex_shader_)
return;
program_ = LinkProgram(frag_shader_, vertex_shader_);
if (!program_)
return;
// Save uniform and attribute locations for future use
texture_loc_ = glGetUniformLocation(program_, "u_texture");
position_loc_ = glGetAttribLocation(program_, "a_position");
texcoord_loc_ = glGetAttribLocation(program_, "a_texcoord");
color_loc_ = glGetAttribLocation(program_, "a_color");
mvp_loc_ = glGetUniformLocation(program_, "u_mvp");
}
The basic shaders in the sample application convert the cube coordinates into world coordinates and paint the cube with the provided textures.
To compile a shader, allocate the needed program memory on the GPU, and load and compile the shader source code. On success, return the shader handle.
To link the shaders to the graphics program, allocate space for the program in the GPU, attach the shaders to it, and call the glLinkProgram() function to initiate the link. On success, return the program handle.
GLuint LinkProgram(GLuint frag_shader, GLuint vert_shader) {
GLuint program = glCreateProgram();
glAttachShader(program, frag_shader);
glAttachShader(program, vert_shader);
glLinkProgram(program);
GLint link_status;
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE) {
// Program failed to link; show the error message
char buffer[1024];
GLsizei length;
glGetProgramInfoLog(program, sizeof(buffer), &length, &buffer[0]);
Logger::Error("Program failed to link: %s", buffer);
return 0;
}
return program;
}
In the InitBuffers() function, allocate the VBO (Vertex Buffer Object) and IBO (Index Buffer Object) for the cube. Load the cube vertex data (position, UV mapping, and color) into the VBO and populate the IBO with the correct triangle indices.
In the InitTexture() function, load the cube texture into the GPU memory. You can load the texture data from the project directory or a remote URL, or you can generate it procedurally. In the sample application, the texture data is loaded from the "texture.cc" file.
The MainLoopIteration() function represents a single iteration of the rendering loop. The Animate() function updates the current X and Y rotation values for the cube in each frame. The Render() function renders the graphic. At the end of the MainLoopIteration() function, the SwapBuffers() function is called on the context. It triggers a completion callback, which calls the MainLoopIteration() function again.
void Graphics3DCubeInstance::MainLoopIteration(int32_t) {
Animate();
Render();
// Swap the background and foreground buffers
context_.SwapBuffers(
callback_factory_.NewCallback(&Graphics3DCubeInstance::MainLoopIteration));
}
Implement the main rendering routine in the Render() function:
Clear the current frame buffer, depth buffer and reset all drawing flags.
Create arrays that contain transformation matrix data.
Using the helper functions defined in the "matrix.h" file, populate the arrays with a rotation matrix based on the current cube rotation, a translation matrix to move the camera away from the cube, and a perspective matrix with the camera perspective settings.
Multiply the rotation, translation, and perspective matrices to obtain a single matrix.
Load the matrix to the uniform address in the GPU program.
Bind the VBO to the context and load all attributes, such as position, color, and UV mapping) into GPU memory.
Bind the IBO to the current context.
Call the glDrawElements() function to draw the cube to the frame buffer.
void Graphics3DCubeInstance::Render() {
// Clear the current buffer
glClearColor(0.5, 0.5, 0.5, 1);
glClearDepthf(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// Set the graphics program to use
glUseProgram(program_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture_);
glUniform1i(texture_loc_, 0);
// Create the perspective matrix
float mvp[16];
float trs[16];
float rot[16];
identity_matrix(mvp);
const float aspect_ratio = static_cast<float>(width_) / height_;
glhPerspectivef2(&mvp[0], kFovY, aspect_ratio, kZNear, kZFar);
// Prepare the transformation matrix
translate_matrix(0, 0, kCameraZ, trs);
rotate_matrix(x_angle_, y_angle_, 0.0f, rot);
multiply_matrix(trs, rot, trs);
multiply_matrix(mvp, trs, mvp);
glUniformMatrix4fv(mvp_loc_, 1, GL_FALSE, mvp);
// Define the attributes of the vertex
// Each attribute has information on its specific data offset in the array
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
glVertexAttribPointer(position_loc_, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, loc)));
glEnableVertexAttribArray(position_loc_);
glVertexAttribPointer(color_loc_, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, color)));
glEnableVertexAttribArray(color_loc_);
glVertexAttribPointer(texcoord_loc_, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, tex)));
glEnableVertexAttribArray(texcoord_loc_);
// Bind the buffer containing the index drawing order and draw the bound elements
// to the background buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
}
Manage Your Cookies
We use cookies to improve your experience on our website and to show you relevant
advertising. Manage you settings for our cookies below.
Essential Cookies
These cookies are essential as they enable you to move around the website. This
category cannot be disabled.
Company
Domain
Samsung Electronics
.samsungdeveloperconference.com
Analytical/Performance Cookies
These cookies collect information about how you use our website. for example which
pages you visit most often. All information these cookies collect is used to improve
how the website works.
Company
Domain
LinkedIn
.linkedin.com
Meta (formerly Facebook)
.samsungdeveloperconference.com
Google Inc.
.samsungdeveloperconference.com
Functionality Cookies
These cookies allow our website to remember choices you make (such as your user name, language or the region your are in) and
tailor the website to provide enhanced features and content for you.
Company
Domain
LinkedIn
.ads.linkedin.com, .linkedin.com
Advertising Cookies
These cookies gather information about your browser habits. They remember that
you've visited our website and share this information with other organizations such
as advertisers.
Company
Domain
LinkedIn
.linkedin.com
Meta (formerly Facebook)
.samsungdeveloperconference.com
Google Inc.
.samsungdeveloperconference.com
Preferences Submitted
You have successfully updated your cookie preferences.