ES之绘制圆柱体,ES之绘制地形

计量顶点位置

算算顶点地方从前,大家先要获取到灰度地形图的像素数量。因为我们需求知道钦赐点的像素颜色。

- (GLubyte *)dataFromImage:(UIImage *)img {
    CGImageRef imageRef = [img CGImage];
    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);

    GLubyte *textureData = (GLubyte *)malloc(width * height * 4);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;

    CGContextRef context = CGBitmapContextCreate(textureData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);
    return textureData;
}

下面的代码将像素格式不分明的图样转换来4通道QashqaiGBA格式的图片数据。textureData的内存布局是R,G,B,A,R,G,B,A,R,G,B,A,...不停重复。地方(x,y)的像素数量在偏移量y * 图片宽度 * 4 + x * 4处。获取极限地点的代码如下。

- (GLKVector3)vertexPosition:(int)col row:(int)row buffer:(unsigned char *)buffer bytesPerRow:(size_t)bytesPerRow bytesPerPixel:(size_t)bytesPerPixel {
    long long offset = (int)(row / self.terrainSize.height * self.heightMap.size.height) * bytesPerRow + (int)(col / self.terrainSize.width * self.heightMap.size.width) * bytesPerPixel;
    unsigned char r = buffer[offset];
    GLfloat x = col;
    GLfloat y = r / 255.0 * self.terrainHeight;
    GLfloat z = row;
    return GLKVector3Make(x, y, z);
}

bytesPerRow是指图片一行的字节数,约等于图片宽度 * 4bytesPerPixel是各样像素的字节数,也等于4。使用red通道的值,总括出y轴上的坐标GLfloat y = r / 255.0 * self.terrainHeight;self.terrainHeight是足以布置的地势高度。self.terrainSize是形势的大小。self.heightMap.size是灰度图片的大大小小。通过统计(int)(row / self.terrainSize.height * self.heightMap.size.height)在图纸上展开采样。

制图圆柱体

有了那七个几何体,就可以组合成三个圆柱体了。上边是绘制代码。

- (void)draw:(GLContext *)glContext {
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    [glContext setUniformMatrix4fv:@"modelMatrix" value:self.modelMatrix];
    bool canInvert;
    GLKMatrix4 normalMatrix = GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert);
    [glContext setUniformMatrix4fv:@"normalMatrix" value:canInvert ? normalMatrix : GLKMatrix4Identity];
    [glContext bindTexture:self.diffuseTexture to:GL_TEXTURE0 uniformName:@"diffuseMap"];
    [glContext drawGeometry:self.topCircle];
    [glContext drawGeometry:self.bottomCircle];
    [glContext drawGeometry:self.middleCylinder];
}

和从前唯一不一样的是[glContext drawGeometry:self.topCircle];情势,那个是增创的用来绘制GLGeometry的主意。达成如下:

- (void)drawGeometry:(GLGeometry *)geometry {
    glBindBuffer(GL_ARRAY_BUFFER, [geometry getVBO]);
    [self bindAttribs:NULL];
    if (geometry.geometryType == GLGeometryTypeTriangleFan) {
        glDrawArrays(GL_TRIANGLE_FAN, 0, [geometry vertexCount]);
    } else if (geometry.geometryType == GLGeometryTypeTriangles) {
        glDrawArrays(GL_TRIANGLES, 0, [geometry vertexCount]);
    } else if (geometry.geometryType == GLGeometryTypeTriangleStrip) {
        glDrawArrays(GL_TRIANGLE_STRIP, 0, [geometry vertexCount]);
    }
}

驷不及舌就是依照区其余geometryType绘制vbo,很好通晓。最终回来ViewController,利用五个圆柱体组装个锤子吧。

- (void)createCylinder {
    GLKTextureInfo *metal1 = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"metal_01.png"].CGImage options:nil error:nil];
    GLKTextureInfo *metal2 = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"metal_02.jpg"].CGImage options:nil error:nil];
    GLKTextureInfo *metal3 = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"metal_03.png"].CGImage options:nil error:nil];
    // 四边的圆柱体就是一个四方体
    Cylinder * cylinder = [[Cylinder alloc] initWithGLContext:self.glContext sides:4 radius:0.9 height:1.2 texture:metal1];
    cylinder.modelMatrix = GLKMatrix4MakeTranslation(0, 2, 0);
    [self.objects addObject:cylinder];

    Cylinder * cylinder2 = [[Cylinder alloc] initWithGLContext:self.glContext sides:16 radius:0.2 height:4.0 texture:metal3];
    [self.objects addObject:cylinder2];

    // 四边的圆柱体就是一个正方体
    Cylinder * cylinder3 = [[Cylinder alloc] initWithGLContext:self.glContext sides:4 radius:0.41 height:0.3 texture:metal2];
    cylinder3.modelMatrix = GLKMatrix4MakeTranslation(0, -2, 0);
    [self.objects addObject:cylinder3];
}

最后效果图如下。

正文通过绘制圆柱体来介绍使用代码生成基本几何体的笔触和章程。下篇文章上校介绍怎样行使一张地形图片生成壹个扑朔迷离的地形模型,敬请期待。

生成网格顶点数据

上篇小说中,大家采纳三角带生成圆柱体的中游部分。以往我们要用四个三角形带来变化地形。

哪些变迁单个三角带我就不赘述了,上篇小说已经介绍了。上边紧要介绍怎么样计算每种终端的地点,法线和UV。

本体系具有作品目录

打造地形几何体

有了获取地方和法线的章程,就能够很有利的创设几何体了。

- (void)buildGeometry {
    CGImageRef image = self.heightMap.CGImage;
    size_t bytesPerRow = CGImageGetBytesPerRow(image);
    size_t bitsPerComponent = CGImageGetBitsPerComponent(image);
    size_t bitesPerPixel = CGImageGetBitsPerPixel(image);
    size_t bytesPerPixel = bitesPerPixel / bitsPerComponent;
    UInt8 * buffer = [self dataFromImage:self.heightMap];
    for (int row = 0;row < self.terrainSize.height; ++row) {
        GLGeometry * terrainMeshStrip = [[GLGeometry alloc] initWithGeometryType:GLGeometryTypeTriangleStrip];
        for (int col = 0;col <= self.terrainSize.width; ++col) {
            GLKVector3 position1 = [self vertexPosition:col row:row buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
            GLKVector3 normal1 = [self vertexNormal:position1 col:col row:row buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
            GLVertex vertex1 = GLVertexMake(position1.x, position1.y, position1.z, normal1.x, normal1.y, normal1.z, col / (GLfloat)self.terrainSize.width * 2, row / (GLfloat)self.terrainSize.height * 2);
            [terrainMeshStrip appendVertex:vertex1];

            GLKVector3 position2 = [self vertexPosition:col row:row + 1 buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
            GLKVector3 normal2 = [self vertexNormal:position2 col:col row:row + 1 buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
            GLVertex vertex2 = GLVertexMake(position2.x, position2.y, position2.z, normal2.x, normal2.y, normal2.z, col / (GLfloat)self.terrainSize.width * 2, (row + 1) / (GLfloat)self.terrainSize.height * 2) ;
            [terrainMeshStrip appendVertex:vertex2];
        }
        [self.terrainMeshStrips addObject:terrainMeshStrip];
    }
    free(buffer);
}

每一行创设2个三角带几何体,总计UV的时候能够乘以七个缩放周到,控制时势贴图的双重次数。通过乘以2col / (GLfloat)self.terrainSize.width * 2,UV的限定就成为了0~2。为了使贴图能够另行,还亟需添加底下的代码。

    GLKTextureInfo *grass = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"grass_01.jpg"].CGImage options:nil error:nil];
    NSError *error;
    GLKTextureInfo *dirt = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"dirt_01.jpg"].CGImage options:nil error:&error];
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, grass.name);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBindTexture(GL_TEXTURE_2D, dirt.name);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

GLKTextureInfo创建后,使用glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);布局它们协理再一次贴图。

Cull Face

暗中同意意况下,投影到显示器后顶点顺序为逆时针的面为正面。

图中左侧的是逆时针,所以若是利用了Cull
Face,大家只能看见左边的面。当然你也得以动用void glFrontFace(GLenum mode​);将顺时针改为正直。

因为自个儿索要顶部圆形的上边一侧显示,所以必须确保从上往下看时,组成三角形的终端顺序是逆时针的。尾部的圆形则相反,从下往上看时,须要保障组成三角形的顶点顺序是逆时针的。

本序列具有文章目录

绘图中间的矩形

当中的矩形可以选择三角带来绘制。

- (GLGeometry *)middleCylinder {
    if (_middleCylinder == nil) {
        _middleCylinder = [[GLGeometry alloc] initWithGeometryType:GLGeometryTypeTriangleStrip];

        float yUP = self.height / 2.0;
        float yDOWN = -self.height / 2.0;
        for (int i = 0; i <= self.sideCount; ++i) {
            GLfloat angle = i / (float)self.sideCount * M_PI * 2;
            GLKVector3 vertexNormal = GLKVector3Normalize(GLKVector3Make(cos(angle) * self.radius, 0, sin(angle) * self.radius));
            GLVertex vertexUp = GLVertexMake(cos(angle) * self.radius, yUP, sin(angle) * self.radius, vertexNormal.x, vertexNormal.y, vertexNormal.z, i / (float)self.sideCount, 0);
            GLVertex vertexDown = GLVertexMake(cos(angle) * self.radius, yDOWN, sin(angle) * self.radius, vertexNormal.x, vertexNormal.y, vertexNormal.z, i / (float)self.sideCount, 1);
            [_middleCylinder appendVertex:vertexDown];
            [_middleCylinder appendVertex:vertexUp];
        }
    }
    return _middleCylinder;
}

可以把它看做self.sideCount个矩形组成的几何体。只必要坚守下图方向依次扩大顶点即可。

注意添加顶点时候自身使用的是从0到2Pi的取向,正如上图所示,那样才能担保顶点顺序是逆时针的。UV直接运用终端在宽高上的比重即可。

多重贴图

为了使时势看起来尤其自然,我添加了草和泥巴二种贴图,并为地形编写了新的fragment
shader。

precision highp float;

varying vec3 fragPosition;
varying vec3 fragNormal;
varying vec2 fragUV;

uniform float elapsedTime;
uniform vec3 lightDirection;
uniform mat4 normalMatrix;
uniform sampler2D grassMap;
uniform sampler2D dirtMap;

void main(void) {
    vec3 normalizedLightDirection = normalize(-lightDirection);
    vec3 transformedNormal = normalize((normalMatrix * vec4(fragNormal, 1.0)).xyz);

    float diffuseStrength = dot(normalizedLightDirection, transformedNormal);
    diffuseStrength = clamp(diffuseStrength, 0.0, 1.0);
    vec3 diffuse = vec3(diffuseStrength);

    vec3 ambient = vec3(0.3);

    vec4 finalLightStrength = vec4(ambient + diffuse, 1.0);


    vec4 grassColor = texture2D(grassMap, fragUV);
    vec4 dirtColor = texture2D(dirtMap, fragUV);
    vec4 materialColor = vec4(0.0);
    if (fragPosition.y <= 30.0) {
        materialColor = dirtColor;
    } else if (fragPosition.y > 30.0 && fragPosition.y < 60.0) {
        float dirtFactor = (60.0 - fragPosition.y) / 30.0;
        materialColor = dirtColor * dirtFactor + grassColor * (1.0 - dirtFactor);
    } else {
        materialColor = grassColor;
    }
    gl_FragColor = vec4(materialColor.rgb * finalLightStrength.rgb, 1.0);
}

日增了多少个纹理uniform sampler2D grassMap; uniform sampler2D dirtMap;,y坐标小于30使用泥土的贴图,30到60使用泥土和草混合,高于60应用草贴图。地形在绘制时还要绑定那两种贴图。

- (void)draw:(GLContext *)glContext {
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    [glContext setUniformMatrix4fv:@"modelMatrix" value:self.modelMatrix];
    bool canInvert;
    GLKMatrix4 normalMatrix = GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert);
    [glContext setUniformMatrix4fv:@"normalMatrix" value:canInvert ? normalMatrix : GLKMatrix4Identity];
    [glContext bindTexture:self.grassTexture to:GL_TEXTURE0 uniformName:@"grassMap"];
    [glContext bindTexture:self.dirtTexture to:GL_TEXTURE1 uniformName:@"dirtMap"];
    for (GLGeometry * geometry in self.terrainMeshStrips) {
        [glContext drawGeometry:geometry];
    }
}

因为地势须求运用不相同等的Fragment
Shader,所以在ViewController中为Terrain转移新的GLContext

- (void)createTerrain {
    NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"vertex" ofType:@".glsl"];
    NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"frag_terrain" ofType:@".glsl"];
    GLContext *terrainContext = [GLContext contextWithVertexShaderPath:vertexShaderPath fragmentShaderPath:fragmentShaderPath];
    GLKTextureInfo *grass = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"grass_01.jpg"].CGImage options:nil error:nil];
    NSError *error;
    GLKTextureInfo *dirt = [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:@"dirt_01.jpg"].CGImage options:nil error:&error];
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, grass.name);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glBindTexture(GL_TEXTURE_2D, dirt.name);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);


    UIImage *heightMap = [UIImage imageNamed:@"terrain_01.jpg"];
    Terrain *terrain = [[Terrain alloc] initWithGLContext:terrainContext heightMap:heightMap size:CGSizeMake(500, 500) height:100 grass:grass dirt:dirt];
    terrain.modelMatrix = GLKMatrix4MakeTranslation(-250, 0, -250);
    [self.objects addObject:terrain];
}

到此,绘制地形就介绍完了。倘诺读者有趣味,能够去网上下载越来越多的灰度地形图来品尝,使用简便的素材生成复杂的地形模型算是绘制地形最有魔力的另一方面了吧。

GLGeometry

为了更便宜的进展极端数据的治本,小编创造了三个GLGeometry类。

typedef enum : NSUInteger {
    GLGeometryTypeTriangles,
    GLGeometryTypeTriangleStrip,
    GLGeometryTypeTriangleFan,
} GLGeometryType;

typedef struct {
    GLfloat x;
    GLfloat y;
    GLfloat z;
    GLfloat normalX;
    GLfloat normalY;
    GLfloat normalZ;
    GLfloat u;
    GLfloat v;
} GLVertex;

@interface GLGeometry () {
    GLuint vbo;
    BOOL vboValid;
}
@property (strong, nonatomic) NSMutableData *vertexData;
@end

@implementation GLGeometry

- (instancetype)initWithGeometryType:(GLGeometryType)geometryType
{
    self = [super init];
    if (self) {
        self.geometryType = geometryType;
        vboValid = NO;
        self.vertexData = [NSMutableData data];
    }
    return self;
}

- (void)dealloc {
    if (vboValid) {
        glDeleteBuffers(1, &vbo);
    }
}

- (void)appendVertex:(GLVertex)vertex {
    void * pVertex = (void *)(&vertex);
    NSUInteger size = sizeof(GLVertex);
    [self.vertexData appendBytes:pVertex length:size];
}

- (GLuint)getVBO {
    if (vboValid == NO) {
        glGenBuffers(1, &vbo);
        vboValid = YES;
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, [self.vertexData length], self.vertexData.bytes, GL_STATIC_DRAW);
    }
    return vbo;
}

- (int)vertexCount {
    return [self.vertexData length] / sizeof(GLVertex);
}

其一类里自身定义了描述顶点数据的结构体GLVertex,描述顶点绘制方式的枚举GLGeometryType,追加顶点数据的艺术- (void)appendVertex:(GLVertex)vertex,生成VBO的方法- (GLuint)getVBO,获取极限个数的形式- (int)vertexCount。有了这一个大家就可以很便宜的打造3D几何体了。

总结法线

因为本身想给各类终端内定唯一的法线,所以必须总计出顶点在每一个面上的法线之和。在网格上各类终端最多被肆个面共享,约等于终极的前后左右各有一个终端。倘若那五个顶峰是Va,Vb,Vc,Vd,中间的点为Vce,那么首先个面的法线就是(Vb - Vce) 叉乘 (Va - Vce),以此类推,算出七个法线,相加后归一化,就足以拿到终极的法线了。因为边缘的极限只怕只被2或二个面共享,所以需求处理一下那种相当处境。上边是法线总括代码。

- (GLKVector3)vertexNormal:(GLKVector3)position col:(int)col row:(int)row buffer:(unsigned char *)buffer bytesPerRow:(size_t)bytesPerRow bytesPerPixel:(size_t)bytesPerPixel {
    GLKVector3 sides[4]; // 最多四条共享边
    int sideCount = 0;
    // 统计顶点有几条共享边,从而计算法线
    if (col >= 1) {
        //左边有共享边
        GLKVector3 leftPosition = [self vertexPosition:col - 1 row:row buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
        GLKVector3 vectorLeft = GLKVector3Subtract(leftPosition, position);
        sides[sideCount] = vectorLeft;
        sideCount++;
    }
    if (row >= 1) {
        //前面有共享边
        GLKVector3 frontPosition = [self vertexPosition:col row:row - 1 buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
        GLKVector3 vectorFront = GLKVector3Subtract(frontPosition, position);
        sides[sideCount] = vectorFront;
        sideCount++;
    }
    if (col <= self.terrainSize.width - 1) {
        //右边有共享边
        GLKVector3 rightPosition = [self vertexPosition:col + 1 row:row buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
        GLKVector3 vectorRight = GLKVector3Subtract(rightPosition, position);
        sides[sideCount] = vectorRight;
        sideCount++;
    }
    if (row <= self.terrainSize.width - 1) {
        //后面有共享边
        GLKVector3 backPosition = [self vertexPosition:col row:row + 1 buffer:buffer bytesPerRow:bytesPerRow bytesPerPixel:bytesPerPixel];
        GLKVector3 vectorBack = GLKVector3Subtract(backPosition, position);
        sides[sideCount] = vectorBack;
        sideCount++;
    }

    GLKVector3 normal = GLKVector3Make(0, 0, 0);
    for (int i = 0; i < sideCount; ++i) {
        GLKVector3 vec = sides[i];
        if (i == sideCount - 1 && i != 3) {
            continue;
        }
        GLKVector3 vec2 = i == sideCount - 1 ? sides[0] : sides[i + 1];
        normal = GLKVector3Add(normal, GLKVector3CrossProduct(vec2, vec));
    }
    return GLKVector3Normalize(normal);
}

GLKVector3CrossProduct即便作为叉乘的法门,多个向量叉乘得出的向量将垂直于它们多少个,相当于法向量。

表明圆柱体

假诺我们有三个纸质的圆柱体模型,大家能够把它剪开成多个圆形和三个矩形。

故而大家可以将圆柱体看做三个几何体来绘制,绘制几个圆圈和多少个卷成桶状的矩形。大家将圆形半径定义为radius,矩形高为height,宽既是圈子的周长。

下边绘制的代码在Cylinder类中,Cylinder继承自GLObject

得到示例代码


正文将介绍怎么着利用一张灰度地形图片生成下边的地形模型。

正文用到的灰度地形图片如下

制图圆形

可以动用多边形逼近的办法绘制圆形,比如大家得以打造2个正36边形来表示多少个圆。本文的代码就是采纳那些原理来绘制圆的。定义构成圆形的边数为sideCount

- (GLGeometry *)topCircle {
    if (_topCircle == nil) {
        _topCircle = [[GLGeometry alloc] initWithGeometryType:GLGeometryTypeTriangleFan];

        float y = self.height / 2.0;
        // 中心点
        GLVertex centerVertex = GLVertexMake(0, y, 0, 0, 1, 0, 0.5, 0.5);
        [_topCircle appendVertex:centerVertex];
        for (int i = self.sideCount; i >= 0; --i) {
            GLfloat angle = i / (float)self.sideCount * M_PI * 2;
            GLVertex vertex = GLVertexMake(cos(angle) * self.radius, y, sin(angle) * self.radius, 0, 1, 0, (cos(angle) + 1 ) / 2.0, (sin(angle) + 1 ) / 2.0);
            [_topCircle appendVertex:vertex];
        }
    }
    return _topCircle;
}

上面是Cylinder.m中的代码,用来营造圆柱体上方的圆形。打造圆形是小编利用的是TriangleFan,可以大大缩小绘制须求的顶点数。首先添加圆心的顶峰,然后围绕主旨顶点,依次出席旁边的极端。上边的法线都以朝上的,既(0, 1, 0)。UV和顶峰的取值如下图所示。

图示为5条边的意况演示,第n条边的Angle等于2 * Pi * n / sideCount,因为sin函数的限量是-1到1,所以利用(sin(angle) + 1 ) / 2.0就可以拿到0~1的uv范围。

人间的圈子和下面首要的差别就是y轴的地方和法线,它座落-height/2处,法线向下。上方的圈子处于height/2处,法线向上。

- (GLGeometry *)bottomCircle {
    if (_bottomCircle == nil) {
        _bottomCircle = [[GLGeometry alloc] initWithGeometryType:GLGeometryTypeTriangleFan];

        float y = -self.height / 2.0;
        // 中心点
        GLVertex centerVertex = GLVertexMake(0, y, 0, 0, -1, 0, 0.5, 0.5);
        [_bottomCircle appendVertex:centerVertex];
        for (int i = 0; i <= self.sideCount; ++i) {
            GLfloat angle = i / (float)self.sideCount * M_PI * 2;
            GLVertex vertex = GLVertexMake(cos(angle) * self.radius, y, sin(angle) * self.radius, 0, -1, 0, (cos(angle) + 1 ) / 2.0, (sin(angle) + 1 ) / 2.0);
            [_bottomCircle appendVertex:vertex];
        }
    }
    return _bottomCircle;
}

细心的读者或然还会发现,循环的次第也不雷同,上边是for (int i = self.sideCount; i >= 0; --i),下面是for (int i = 0; i <= self.sideCount; ++i)。为啥要如此吗?因为本身打开了删减表面,glEnable(GL_CULL_FACE);,并且剔除的是背面glCullFace(GL_BACK);。剔除背面就代表背面将不会被渲染,唯有正面面向录制机的时候大家才能来看它被渲染。那么OpenGL如何判断正面还是背面呢?

- (void)draw:(GLContext *)glContext {
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    ...
}

什么样是地形模型

地形模型一般是由NxN的网格构成,网格的点在y轴上的坐标由灰度地形图上相应的颜料决定。颜色越亮,高度越高。颜色逐个通道的取值范围能够是0~
255,通过公式转换,可以很简单的支配转变模型的万丈。

拿到示例代码


正文将要介绍如何使用代码绘制一个圆柱体,通过绘制圆柱体可以更好的主宰法线,UV,TriangleFan,TriangleStrip等相关文化。在绘制此前,先举行一些准备干活。