本文转自简书,原文地址http://www.jianshu.com/p/e4a8c83cd37

本文是关于OpenGL ES的系统性学习过程,记录了自己在学习OpenGL ES时的收获。
这篇文章的目标是用OpenGL ES 2.0实现常见的特效。
环境是Xcode8.1+OpenGL ES 2.0
目前代码已经放到github上面,OpenGL ES入门09-GLSL实现常见特效

欢迎关注我的 OpenGL ES入门专题

概述

这篇文章主要是使用OpenGL ES2.0实现一些常见的滤镜特效:1、灰度图;2、图像颠倒;3、图像漩涡;4、马赛克效果;5、浮雕效果。这些效果不是很难,但是对于学习GLSL是很有帮助的。

灰度图

任何颜色都有红、绿、蓝三原色组成,假如原来某点的颜色为RGB(R,G,B),那么,我们可以通过下面几种方法,将其转换为灰度:
1.浮点算法:Gray=R*0.3+G*0.59+B*0.11
2.整数方法:Gray=(R*30+G*59+B*11)/100
3.移位方法:Gray =(R*76+G*151+B*28)>>8;
4.平均值法:Gray=(R+G+B)/3;
5.仅取绿色:Gray=G;
通过上述任一种方法求得Gray后,将原来的RGB(R,G,B)中的R,G,B统一用Gray替换,形成新的颜色RGB(Gray,Gray,Gray),用它替换原来的RGB(R,G,B)就是灰度图了。在这里我们使用GPUImage中使用的变换因子:

const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);

灰度图.png
//灰度图
precision highp float;
uniform sampler2D image;
varying vec2 vTexcoord;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721); void main()
{
lowp vec4 textureColor = texture2D(image, vTexcoord);
float luminance = dot(textureColor.rgb, W); gl_FragColor = vec4(vec3(luminance), textureColor.a);
}

图像颠倒

图像颠倒实现比较简单,在文理采样的时候我们只需要反转UV坐标,便可以实现图像颠倒的效果。

图像颠倒.png
precision mediump float;

varying vec2 vTexcoord;
uniform sampler2D image; void main()
{
vec4 color = texture2D(image, vec2(vTexcoord.x, 1.0 - vTexcoord.y));
gl_FragColor = color;
}

图像漩涡

图像漩涡主要是在某个半径范围里,把当前采样点旋转一定角度,旋转以后当前点的颜色就被旋转后的点的颜色代替,因此整个半径范围里会有旋转的效果。如果旋转的时候旋转角度随着当前点离半径的距离递减,整个图像就会出现漩涡效果。这里使用的了抛物线递减因子:(1.0-(r/Radius)*(r/Radius) )。

漩涡.png
precision mediump float;

const float PI = 3.14159265;
uniform sampler2D image; const float uD = 80.0; //旋转角度
const float uR = 0.5; //旋转半径 varying vec2 vTexcoord; void main()
{
ivec2 ires = ivec2(512, 512);
float Res = float(ires.s); vec2 st = vTexcoord;
float Radius = Res * uR; vec2 xy = Res * st; vec2 dxy = xy - vec2(Res/2., Res/2.);
float r = length(dxy); float beta = atan(dxy.y, dxy.x) + radians(uD) * 2.0 * (-(r/Radius)*(r/Radius) + 1.0);//(1.0 - r/Radius); vec2 xy1 = xy;
if(r<=Radius)
{
xy1 = Res/2. + r*vec2(cos(beta), sin(beta));
} st = xy1/Res; vec3 irgb = texture2D(image, st).rgb; gl_FragColor = vec4( irgb, 1.0 );
}

马赛克

马赛克效果就是把图片的一个相当大小的区域用同一个点的颜色来表示.可以认为是大规模的降低图像的分辨率,而让图像的一些细节隐藏起来。

马赛克.png
precision mediump float;

varying vec2 vTexcoord;
uniform sampler2D image;
const vec2 TexSize = vec2(400.0, 400.0);
const vec2 mosaicSize = vec2(8.0, 8.0); void main()
{
vec2 intXY = vec2(vTexcoord.x*TexSize.x, vTexcoord.y*TexSize.y);
vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);
vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);
vec4 color = texture2D(image, UVMosaic);
gl_FragColor = color;
}

浮雕效果

浮雕效果是指图像的前景前向凸出背景。实现思路:把图象的一个象素和左上方的象素进行求差运算,并加上一个灰度。这个灰度就是表示背景颜色。这里我们设置这个插值为128 (图象RGB的值是0-255)。同时,我们还应该把这两个颜色的差值转换为亮度信息,避免浮雕图像出现彩色像素。

浮雕.png
//浮雕效果
precision mediump float;
varying vec2 vTexcoord;
uniform sampler2D image;
const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);
const vec2 TexSize = vec2(100.0, 100.0);
const vec4 bkColor = vec4(0.5, 0.5, 0.5, 1.0); void main()
{
vec2 tex = vTexcoord;
vec2 upLeftUV = vec2(tex.x-1.0/TexSize.x, tex.y-1.0/TexSize.y);
vec4 curColor = texture2D(image, vTexcoord);
vec4 upLeftColor = texture2D(image, upLeftUV);
vec4 delColor = curColor - upLeftColor;
float luminance = dot(delColor.rgb, W);
gl_FragColor = vec4(vec3(luminance), 0.0) + bkColor;
}

完整代码

//
// OpenGLESView.m
// OpenGLES01-环境搭建
//
// Created by qinmin on 2017/2/9.
// Copyright © 2017年 qinmin. All rights reserved.
// #import "OpenGLESView.h"
#import <OpenGLES/ES2/gl.h>
#import "GLUtil.h"
#include "JpegUtil.h" @interface OpenGLESView ()
{
CAEAGLLayer *_eaglLayer;
EAGLContext *_context;
GLuint _colorRenderBuffer;
GLuint _frameBuffer; GLuint _program;
GLuint _vbo;
GLuint _texture;
GLuint _texture1;
int _vertCount;
}
@end @implementation OpenGLESView + (Class)layerClass
{
// 只有 [CAEAGLLayer class] 类型的 layer 才支持在其上描绘 OpenGL 内容。
return [CAEAGLLayer class];
} - (void)dealloc
{
glDeleteBuffers(1, &_vbo);
glDeleteTextures(1, &_texture);
glDeleteProgram(_program);
} - (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setupLayer];
[self setupContext];
[self setupGLProgram];
[self setupVBO];
[self setupTexure];
[self setupTexure1];
}
return self;
} - (void)layoutSubviews
{
[EAGLContext setCurrentContext:_context]; [self destoryRenderAndFrameBuffer]; [self setupFrameAndRenderBuffer]; [self render];
} #pragma mark - Setup
- (void)setupLayer
{
_eaglLayer = (CAEAGLLayer*) self.layer; // CALayer 默认是透明的,必须将它设为不透明才能让其可见
_eaglLayer.opaque = YES; // 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8
_eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
} - (void)setupContext
{
// 设置OpenGLES的版本为2.0 当然还可以选择1.0和最新的3.0的版本,以后我们会讲到2.0与3.0的差异,目前为了兼容性选择2.0的版本
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!_context) {
NSLog(@"Failed to initialize OpenGLES 2.0 context");
exit(1);
} // 将当前上下文设置为我们创建的上下文
if (![EAGLContext setCurrentContext:_context]) {
NSLog(@"Failed to set current OpenGL context");
exit(1);
}
} - (void)setupFrameAndRenderBuffer
{
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
// 为 color renderbuffer 分配存储空间
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer]; glGenFramebuffers(1, &_frameBuffer);
// 设置为当前 framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, _colorRenderBuffer);
} - (void)setupGLProgram
{
NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"vert" ofType:@"glsl"];
NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"cameo" ofType:@"glsl"]; _program = createGLProgramFromFile(vertFile.UTF8String, fragFile.UTF8String);
glUseProgram(_program);
} - (void)setupVBO
{
_vertCount = 6; GLfloat vertices[] = {
0.8f, 0.6f, 0.0f, 1.0f, 0.0f, // 右上
0.8f, -0.6f, 0.0f, 1.0f, 1.0f, // 右下
-0.8f, -0.6f, 0.0f, 0.0f, 1.0f, // 左下
-0.8f, -0.6f, 0.0f, 0.0f, 1.0f, // 左下
-0.8f, 0.6f, 0.0f, 0.0f, 0.0f, // 左上
0.8f, 0.6f, 0.0f, 1.0f, 0.0f, // 右上
}; // 创建VBO
_vbo = createVBO(GL_ARRAY_BUFFER, GL_STATIC_DRAW, sizeof(vertices), vertices); glEnableVertexAttribArray(glGetAttribLocation(_program, "position"));
glVertexAttribPointer(glGetAttribLocation(_program, "position"), 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL); glEnableVertexAttribArray(glGetAttribLocation(_program, "texcoord"));
glVertexAttribPointer(glGetAttribLocation(_program, "texcoord"), 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL+sizeof(GL_FLOAT)*3);
} - (void)setupTexure
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"jpg"]; unsigned char *data;
int size;
int width;
int height; // 加载纹理
if (read_jpeg_file(path.UTF8String, &data, &size, &width, &height) < 0) {
printf("%s\n", "decode fail");
} // 创建纹理
_texture = createTexture2D(GL_RGB, width, height, data); if (data) {
free(data);
data = NULL;
}
} - (void)setupTexure1
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"noise" ofType:@"jpg"]; unsigned char *data;
int size;
int width;
int height; // 加载纹理
if (read_jpeg_file(path.UTF8String, &data, &size, &width, &height) < 0) {
printf("%s\n", "decode fail");
} // 创建纹理
_texture1 = createTexture2D(GL_RGB, width, height, data); if (data) {
free(data);
data = NULL;
}
} #pragma mark - Clean
- (void)destoryRenderAndFrameBuffer
{
glDeleteFramebuffers(1, &_frameBuffer);
_frameBuffer = 0;
glDeleteRenderbuffers(1, &_colorRenderBuffer);
_colorRenderBuffer = 0;
} #pragma mark - Render
- (void)render
{
glClearColor(1.0, 1.0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(2.0); glViewport(0, 0, self.frame.size.width, self.frame.size.height); // 激活纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _texture);
glUniform1i(glGetUniformLocation(_program, "image"), 0); // 激活纹理
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _texture1);
glUniform1i(glGetUniformLocation(_program, "image1"), 1); glDrawArrays(GL_TRIANGLES, 0, _vertCount); //将指定 renderbuffer 呈现在屏幕上,在这里我们指定的是前面已经绑定为当前 renderbuffer 的那个,在 renderbuffer 可以被呈现之前,必须调用renderbufferStorage:fromDrawable: 为之分配存储空间。
[_context presentRenderbuffer:GL_RENDERBUFFER];
} @end

参考资料

GPUImage

OpenGL ES入门09-GLSL实现常见特效 [转]的更多相关文章

  1. OpenGL ES入门详解

     http://blog.csdn.net/wangyuchun_799/article/details/7736928 1.决定你要支持的OpenGL ES的版本.目前,OpenGL ES包含1.1 ...

  2. OpenGL ES 入门

    写在前面 记录一下 OpenGL ES Android 开发的入门教程.逻辑性可能不那么强,想到哪写到哪.也可能自己的一些理解有误. 参考资料: LearnOpenGL CN Android官方文档 ...

  3. IOS 中openGL使用教程1(openGL ES 入门篇 | 搭建openGL环境)

    OpenGL版本 iOS系统默认支持OpenGl ES1.0.ES2.0以及ES3.0 3个版本,三者之间并不是简单的版本升级,设计理念甚至完全不同,在开发OpenGL项目前,需要根据业务需求选择合适 ...

  4. Android OpenGL ES 入门系列(二) --- 环境搭建

    转载请注明出处 本文出自Hansion的博客 本章介绍如何使用GLSurfaceView和GLSurfaceView.Renderer完成在Activity中的最简单实现. 1.在AndroidMan ...

  5. Android OpenGL ES 入门系列(一) --- 了解OpenGL ES的前世今生

    转载请注明出处 本文出自Hansion的博客 OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机.PDA和游戏主机等嵌 ...

  6. iOS开发-OpenGL ES入门教程1

    http://www.jianshu.com/p/750fde1d8b6a 这里是一篇新手教程,环境是Xcode7+OpenGL ES 2.0,目标写一个OpenGL ES的hello world.O ...

  7. IOS 中openGL使用教程2(openGL ES 入门篇 | 绘制一个多边形)

    在上一篇我们学习了如何搭建IOS下openGL的开发环境,接下来我们来学习如何绘制一个多边形. 在2.0之前,es的渲染采用的是固定管线,何为固定管线,就是一套固定的模板流程,局部坐标变换 -> ...

  8. IOS 中openGL使用教程3(openGL ES 入门篇 | 纹理贴图(texture)使用)

    在这篇文章中,我们将学习如何在openGL中使用纹理贴图. penGL中纹理可以分为1D,2D和3D纹理,我们在绑定纹理对象的时候需要指定纹理的种类.由于本文将以一张图片为例,因此我们为我们的纹理对象 ...

  9. IOS 中openGL使用教程4(openGL ES 入门篇 | 离屏渲染)

    通常情况下,我们使用openGL将渲染好的图片绘制到屏幕上,但有时候我们不想显示处理结果,这时候就需要使用离屏渲染了. 正常情况下,我们将屏幕,也就是一个CAEAGLLayer对象作为渲染目标,离屏渲 ...

随机推荐

  1. Django Ajax学习二之csrf跨站请求伪造

    方式1 $.ajaxSetup({ data: {csrfmiddlewaretoken: '{{ csrf_token }}' }, }); 方式2 # html文件from表单中<form& ...

  2. LeetCode解题报告—— Combination Sum & Combination Sum II & Multiply Strings

    1. Combination Sum Given a set of candidate numbers (C) (without duplicates) and a target number (T) ...

  3. Docker 常用命令收集

    查看Docker版本 docker version 查看 Image docker images 打包 Image docker save -o ‘packageName.tar’ ‘imageNam ...

  4. 前端读者 | Web App开发入门

    本文来自互联网 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联网界从此就多了一个新的名词 - Web App(意为基于WEB形式的应用程序).业界关于Web App与Nativ ...

  5. php smtp发送邮件功能

    <?php header("Content-Type: text/html; charset=utf-8"); class smtp { /* Public Variable ...

  6. urllib2模块、cookielib模块

    urllib2模块 urllib模块和urllib模块类似,用来打开URL并从中获取数据.与urllib模块不同的是,urllib模块不仅可以使用urlopen() 函数还可以自定义Opener来访问 ...

  7. Python和xml简介

    python提供越来越多的技术来支持xml,本文旨在面向初学利用Python处理xml的读者,以教程的形式介绍一些基本的xml出来概念.前提是读者必须知道一些xml常用术语. 先决条件 本文所有的例子 ...

  8. 17-7-27-日常学习react

    啊啊啊啊啊啊啊,今天改了一天的css,因为项目是由两个开源项目整合而成,所以CSS合并的时候,超级超级奇怪,就一直在调.不过也学会了怎么调css.之前觉得css很困难,不过调了一天,感觉还好,就是自己 ...

  9. Shiro的认证原理(Subject#login的背后故事)

    登录操作一般都是我们触发的: Subject subject = SecurityUtils.getSubject(); AuthenticationToken authenticationToken ...

  10. 【POJ 2409】 Let it Bead(置换、burnside引理)

    Let it Bead "Let it Bead" company is located upstairs at 700 Cannery Row in Monterey, CA. ...