Creating 3D Graphics with OpenGL ES on Xcode: A Step-by-Step Guide for iOS Developers

Introduction to OpenGL ES on Xcode

OpenGL ES (Embedded Systems) is a cross-platform API for rendering 2D and 3D graphics. It is widely used in mobile devices, including iOS and Android platforms. In this article, we will explore how to create a 3D sphere and apply texture to it using OpenGL ES on Xcode.

Prerequisites

Before diving into the code, make sure you have:

  • Xcode installed on your Mac
  • A basic understanding of Objective-C programming language
  • A familiarity with iOS development

Setting Up the Project

Create a new project in Xcode and select “Application” under the template selection page. In the next step, choose “iOS App” as the product type.

Next, create an empty file named Shader.m and write the following code:

#import <Foundation/Foundation.h>

@interface Shader : NSObject {
    NSString *vertexShaderSource;
    NSString *fragmentShaderSource;
}

@property (nonatomic, copy) NSString *vertexShaderSource;
@property (nonatomic, copy) NSString *fragmentShaderSource;

@end

@implementation Shader

@synthesize vertexShaderSource = _vertexShaderSource;
@synthesize fragmentShaderSource = _fragmentShaderSource;

- (instancetype)init {
    self = [super init];
    if (self) {
        // Initialize shaders here
    }
    return self;
}

- (void)setVertexShaderSource:(NSString *)vertexShaderSource {
    _vertexShaderSource = vertexShaderSource;
}

- (void)setFragmentShaderSource:(NSString *)fragmentShaderSource {
    _fragmentShaderSource = fragmentShaderSource;
}

@end

Creating a Sphere

To create a sphere, we will use the GLKMatrix4 class to define a 4x4 matrix. The sphere’s position and radius are defined using this matrix.

Firstly, add the following code in your ViewController.m file:

#import <GLKit/GLKit.h>

@interface ViewController () {
    GLKMatrix4 modelMatrix;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Initialize sphere model matrix here
}

Next, write a function to create the sphere model matrix:

- (GLKMatrix4)sphereModelMatrix:(float)radius {
    GLKMatrix4 sphereModel = GLKMatrix4MakeIdentity();
    
    sphereModel.m00 = 1 / radius;
    sphereModel.m11 = -1 / radius;
    sphereModel.m22 = 1 / radius;
    sphereModel.m33 = (float)sqrt(1 - (float)pow(radius, 2));
    sphereModel.m02 = 0.0;
    sphereModel.m13 = 0.0;
    
    return sphereModel;
}

Now, create a function to draw the sphere:

- (void)drawSphere {
    GLKMatrix4 modelMatrix = [self sphereModelMatrix:100.0f];
    
    // Set up vertex data for the sphere
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, NULL);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void*)(&modelMatrix.m00));
    
    // Create and set up the shader
    Shader *shader = [[Shader alloc] init];
    [shader setVertexShaderSource:@"#version 150\nin vec4 vertexPosition;\nout vec2 texCoord0;\nvoid main() {\n    gl_Position = projectionMatrix * modelViewMatrix * vertexPosition;\n    texCoord0 = (vertexPosition.xy - vec2(1.0, 1.0)) / vec2(-1.0, -1.0);\n}"];
    
    [shader setFragmentShaderSource:@"#version 150\nin vec2 texCoord0;\nout vec4 fragColor;\nvoid main() {\n    fragColor = texture(iResolution.xy, texCoord0) * 0.5 + 0.5;\n}"];
    
    // Set up the view matrix
    GLKMatrix4 viewMatrix = GLKMatrix4MakeTranslation(0, -100, -400);
    
    // Create and set up the model-view-projection matrix
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspectiveFOV(GL_PI / 2.0f, GLKMatrix4Identity(), 0.1f, 10000.0f);
    GLKMatrix4 projectionViewMatrix = GLKMatrix4Multiply(projectionMatrix, viewMatrix);
    
    // Set up the model-view matrix
    GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(modelMatrix, GLKMatrix4MakeTranslation(0, -100, -400));
    
    // Create and set up the uniform matrices
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "projection"), 1, GL_FALSE, projectionViewMatrix.data);
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "model"), 1, GL_FALSE, modelViewMatrix.data);
}

Applying Texture

To apply a texture to the sphere, we will use the GLKVector2 class to define the UV coordinates for each face of the sphere.

Firstly, write an OpenGL ES code snippet in your ViewController.m file:

- (void)drawSphere {
    // ... existing drawSphere function
    
    // Set up vertex data for the texture coordinates
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, NULL);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(&modelMatrix.m00));
}

Next, write an OpenGL ES code snippet to create the texture map:

- (void)drawSphere {
    // ... existing drawSphere function
    
    // Set up vertex data for the texture coordinates
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, NULL);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(&modelMatrix.m00));
    
    // Create and set up the shader
    Shader *shader = [[Shader alloc] init];
    [shader setVertexShaderSource:@"#version 150\nin vec4 vertexPosition;\nout vec2 texCoord0;\nvoid main() {\n    gl_Position = projectionMatrix * modelViewMatrix * vertexPosition;\n    texCoord0 = (vertexPosition.xy - vec2(1.0, 1.0)) / vec2(-1.0, -1.0);\n}"];
    
    [shader setFragmentShaderSource:@"#version 150\nin vec2 texCoord0;\nout vec4 fragColor;\nvoid main() {\n    // Calculate the UV coordinates based on the vertex position
    float u = (texCoord0.x + 0.5) / 1;
    float v = (texCoord0.y + 0.5) / 1;
    
    // Apply a texture to the sphere
    vec4 textureValue = texture2D(iResolution, vec2(u, v));
    
    fragColor = textureValue * 0.5 + 0.5;\n}"];
    
    // Set up the view matrix
    GLKMatrix4 viewMatrix = GLKMatrix4MakeTranslation(0, -100, -400);
    
    // Create and set up the model-view-projection matrix
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspectiveFOV(GL_PI / 2.0f, GLKMatrix4Identity(), 0.1f, 10000.0f);
    GLKMatrix4 projectionViewMatrix = GLKMatrix4Multiply(projectionMatrix, viewMatrix);
    
    // Set up the model-view matrix
    GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(modelMatrix, GLKMatrix4MakeTranslation(0, -100, -400));
    
    // Create and set up the uniform matrices
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "projection"), 1, GL_FALSE, projectionViewMatrix.data);
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "model"), 1, GL_FALSE, modelViewMatrix.data);
}

Using a Texture

To use the generated texture map, create and set up the texture object in your ViewController.m file:

- (void)drawSphere {
    // ... existing drawSphere function
    
    // Create and set up the shader
    Shader *shader = [[Shader alloc] init];
    [shader setVertexShaderSource:@"#version 150\nin vec4 vertexPosition;\nout vec2 texCoord0;\nvoid main() {\n    gl_Position = projectionMatrix * modelViewMatrix * vertexPosition;\n    texCoord0 = (vertexPosition.xy - vec2(1.0, 1.0)) / vec2(-1.0, -1.0);\n}"];
    
    [shader setFragmentShaderSource:@"#version 150\nin vec2 texCoord0;\nout vec4 fragColor;\nvoid main() {\n    // Calculate the UV coordinates based on the vertex position
    float u = (texCoord0.x + 0.5) / 1;
    float v = (texCoord0.y + 0.5) / 1;
    
    // Load the texture
    vec4 textureValue = texture2D(iResolution, vec2(u, v));
    
    // Apply a texture to the sphere
    fragColor = textureValue * 0.5 + 0.5;\n}"];
    
    // Set up the view matrix
    GLKMatrix4 viewMatrix = GLKMatrix4MakeTranslation(0, -100, -400);
    
    // Create and set up the model-view-projection matrix
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspectiveFOV(GL_PI / 2.0f, GLKMatrix4Identity(), 0.1f, 10000.0f);
    GLKMatrix4 projectionViewMatrix = GLKMatrix4Multiply(projectionMatrix, viewMatrix);
    
    // Set up the model-view matrix
    GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(modelMatrix, GLKMatrix4MakeTranslation(0, -100, -400));
    
    // Create and set up the uniform matrices
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "projection"), 1, GL_FALSE, projectionViewMatrix.data);
    glUniformMatrix4fv(glGetUniformLocation(shader.shaderProgram, "model"), 1, GL_FALSE, modelViewMatrix.data);
}

Conclusion

This article provided a basic example of how to create a 3D sphere and apply texture using OpenGL ES on Xcode. It covered the creation of the sphere model matrix, setting up vertex data for the sphere, creating and setting up shaders, and applying textures.

It’s worth noting that this is just a basic example and there are many ways to improve it, such as by adding more complex geometry or using other rendering techniques like instancing or deferred shading.

For any questions or comments please don’t hesitate to reach out.


Last modified on 2024-01-12