上学OpenGL ES之绘制地形

本系列具有文章目录

1、搜索共享,打开后登屏幕共享配置界面,默认是既关门状态,设置状态呢移动并安排密码:

构建地形几何体

出了收获位置以及法线的办法,就好非常有利的构建几何体了。

- (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);
}

各一样实行构建一个三角带几何体,计算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);布置它们支持再次贴图。

 

好家伙是地形模型

地形模型一般是出于NxN的网格构成,网格的触发在y轴上之坐标由灰度地形图上相应的水彩决定。颜色越显,高度越来越强。颜色每个通道的取值范围可是0~
255,通过公式转换,可以很易之操纵转变模型的冲天。

图片 1

大多又贴图

为要形势看起更自然,我上加了拟和粘土两栽贴图,并也地形编写了初的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];
}

交者,绘制地形就是介绍完了。如果读者出趣味,可以错过网上下载更多的灰度地形图来尝试,使用简单的资料生成复杂的地形模型算是绘制地形最有魅力之单方面了吧。

 

生成网格顶点数据

齐篇文章被,我们用三角带生成圆柱体的中档部分。现在我们而因此多单三角形带来变化地形。

哪转移单个三角带我虽不赘述了,上篇文章曾介绍了。下面要介绍如何算每个终端的职,法线和UV。

root@kali:~# 
root@kali:~# vim /etc/apt/sources.list

# deb cdrom:[Debian GNU/Linux 2017.3 _Kali-rolling_ - Official Snapshot amd64 LIVE/INSTALL Binary 20171109-13:49]/ kali-rolling contrib main non-free

#deb cdrom:[Debian GNU/Linux 2017.3 _Kali-rolling_ - Official Snapshot amd64 LIVE/INSTALL Binary 20171109-13:49]/ kali-rolling contrib main non-free
#中科大
deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib
deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib

#浙大
deb http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free
deb-src http://mirrors.zju.edu.cn/kali kali-rolling main contrib non-free

#东软大学
deb http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib
deb-src http://mirrors.neusoft.edu.cn/kali kali-rolling/main non-free contrib

#重庆大学
deb http://http.kali.org/kali kali-rolling main non-free contrib
deb-src http://http.kali.org/kali kali-rolling main non-free contrib

#官方源
#deb http://http.kali.org/kali kali-rolling main non-free contrib
#deb-src http://http.kali.org/kali kali-rolling main non-free contrib

计算法线

以自怀念为每个终端指定唯一的法线,所以必须计算出顶点在每个面上的法线之和。在网格上每个终端最多让4个当共享,也即是终极的前后左右各有一个终端。假设这四独极是Va,Vb,Vc,Vd,中间的点为Vce,那么首先单对之法线就是(Vb - Vce) 叉乘 (Va - Vce),以此类推,算有四单法线,相加后归一化,就可以拿走最终的法线了。因为边缘之极可能仅受2还是3单照共享,所以需要处理一下这种不同寻常情况。下面是法线计算代码。

- (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哪怕当叉乘的艺术,两单向量叉乘得出的向量将直于其简单独,也就是是法向量。

图片 2

算顶点位置

算算顶点位置之前,我们事先要收获到灰度地形图的像素数量。因为咱们得理解指定点的诸如素颜色。

- (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通路RGBA格式的图片数。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)当图片上展开采样。

 

获得示例代码


正文将介绍如何用同样布置灰度地形图片转下面的地形模型。

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

 

 

  

图片 3

图片 4

 

3、更新:

3、使用VNC Viewer远程连接:

 

root@kali:~# lsb_release -a
No LSB modules are available.
Distributor ID: Kali
Description:    Kali GNU/Linux Rolling
Release:        kali-rolling
Codename:       kali-rolling

2、添加更新源,更新源内容可以打网上找到:

提交修改。

1、查看系统版本

2、搜索dconf打开系统布局编辑器,进入/org/gnome/desktop/remote-access/,编辑require-encryption配置起,把值设为false:

 

 

图片 5

敞开远程桌面共享

4、安装dconf-editor

更新源并设置系统布局工具(不更新源无法进展设置)

apt-get install dconf-editor

apt-get clean && apt-get update

通过启用屏幕共享来开远程桌面登录,开启后需关闭encryption,否则会现出无法连接的状态。关闭encryption可以下系统布局工具dconf来形成。所以先安装dconf-editor。