Linux OpenGL 实践篇-12-procedural-texturing
程序式纹理
简单的来说程序式纹理就是用数学公式描述物体表面的纹路 。而实现这个过程的着色器我们称之为程序纹理着色器,通常在这类着色器中我们能使用的输入信息也就是顶点坐标和纹理坐标。
程序式纹理的优点
1.程序式纹理的内存占用比预存纹理要低的多;因为程序式纹理主要是算法的实现,数据都是通过计算产生的;
2.程序生成的纹理没有固定的面积和分辨率,可以随意的应用到不同大小的物体,而不用担心精度不够的问题;
3.程序式纹理可以写入一些算法的关键参数,可以方便的供程序修改从而创建出有趣的效果,而预存的纹理则很难进行改动;
4.如果我们使用程序式纹理计算体积而不是表面的话,那么体积的剖面表现力会比任何使用2维纹理的方式要真实的多。
程序式纹理缺点
1.程序式纹理对编程人员的要求较高,特别是算法要求;
2.程序式纹理的数据是实时计算产生的,所以每一次都需要重新计算,相对预存纹理时间会花费的比较多;
3.程序式纹理可能会带来一些难以克服的走样问题;而对于预存纹理现在的图形硬件都有比较成熟的反走样算法;
4.由于数学精度上的差异和噪声实现算法上的差异,在不同平台上程序式纹理的表现不一定一致。
简单的程序式纹理
砖块
顶点着色器:
#version core layout(location=) in vec3 iPos;
layout(location=) in vec2 iTexcoord; uniform mat4 model;
uniform mat4 view;
uniform mat4 proj; out vec2 texcoord;
out vec2 mcPos; void main()
{
texcoord = iTexcoord;
mcPos = iPos.xy;
gl_Position = proj * view * model * vec4(iPos,1.0);
}
片元着色器:
#version core in vec2 texcoord;
in vec2 mcPos;
out vec4 color; uniform vec3 brickColor,mortarColor;
uniform vec2 brickSize;
uniform vec2 brickPct; void main()
{
vec2 pos, useBrick;
pos = mcPos / brickSize; if(fract(pos.y * 0.5) > 0.5)
{
pos.x += 0.5 ;
} pos = fract(pos);
useBrick = step(pos,brickPct); vec3 c = mix(mortarColor,brickColor,useBrick.x * useBrick.y);
color = vec4(c,1.0);
}
效果图:

晶格
顶点着色器:
#version core layout(location=) in vec3 iPos;
layout(location=) in vec2 iTexcoord; uniform mat4 model;
uniform mat4 view;
uniform mat4 proj; out vec2 texcoord; void main()
{
texcoord = iTexcoord;
gl_Position = proj * view * model * vec4(iPos,1.0);
}
片元着色器:
#version core in vec2 texcoord;
out vec4 color; uniform vec2 scale;
uniform vec2 threshold; void main()
{
float ss = fract(texcoord.s * scale.s);
float tt = fract(texcoord.t * scale.t); if((ss > threshold.s) && (tt > threshold.t))
discard; color = vec4(,,,);
}
效果图:

噪声
使用计算机渲染精致的物体式非常的容易的,但在真实的世界中物体往往不是这样的,它们经常是带污渍、凹痕、磨损的,如果要实现这样的效果,艺术家通常要花费很多的时间来进行构建。针对这个问题,Ken Perlin在20世纪80年代进行了深入的研究,提出了一种直到现在也很有用的技术——噪声。噪声我们可以认为是一些无规律的数据,类似老电视机中没有信号时出现的随机的雪花像素点。但这种随机的数据对于计算机图形学并没有什么用处,在计算机图形学当中,我们需要的是一种可重复的函数。比如,对于某个物体的表面,我们希望随机分布是空间上的,而不是时间上的,除非有特定的需求。根据以上的需求,理想的噪声函数应该具备下面一些重要的特性:
1.噪声不会有任何明显的规则或者重复花样;
2.噪声是一个连续函数,它的导数也是连续的;
3.噪声函数的结果可以随时间变化复现(也就是说,每一次输入的数据一致时,它返回的值也是相同的)。
4.噪声的输出数据需要一个明确的空间定义(通常是[-1,1]或[0,1]);
5.噪声函数的小规模形式不会受到大范围的位置数据影响;
6.噪声函数是各向同性的(它的统计特性在所有的方向都是相同的);
7.噪声可以定义为1、2、3、4或者更高维度;
8.对于任何给定的输入,噪声的计算都是非常迅速。
在OpenGL中使用以下三种方式为程序添加噪声:
1.自己实现noise函数;
2.使用内置OpenGL函数noise实现;
3.使用纹理预存噪声数据;
下面是自己实现的一个 perlin噪声函数:
/* coherent noise function over 1, 2 or 3 dimensions */
/* (copyright Ken Perlin) */ #include <stdlib.h>
#include <stdio.h>
#include <math.h> #define B 0x100
#define BM 0xff #define N 0x1000
#define NP 12 /* 2^N */
#define NM 0xfff static int p[B + B + ];
static float g3[B + B + ][];
static float g2[B + B + ][];
static float g1[B + B + ];
static int start = ; static void init(void); #define s_curve(t) ( t * t * (3. - 2. * t) ) #define lerp(t, a, b) ( a + t * (b - a) ) #define setup(i,b0,b1,r0,r1)\
t = vec[i] + N;\
b0 = ((int)t) & BM;\
b1 = (b0+) & BM;\
r0 = t - (int)t;\
r1 = r0 - .; double noise1(double arg)
{
int bx0, bx1;
float rx0, rx1, sx, t, u, v, vec[]; vec[] = arg;
+--- 行: if (start) {------------------------------------------------ setup(, bx0,bx1, rx0,rx1); sx = s_curve(rx0); u = rx0 * g1[ p[ bx0 ] ];
v = rx1 * g1[ p[ bx1 ] ]; return lerp(sx, u, v);
} float noise2(float vec[])
{
int bx0, bx1, by0, by1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
register int i, j; +--- 行: if (start) {---------------------------------------------------------------------- setup(, bx0,bx1, rx0,rx1);
setup(, by0,by1, ry0,ry1); i = p[ bx0 ];
j = p[ bx1 ]; b00 = p[ i + by0 ];
b10 = p[ j + by0 ];
b01 = p[ i + by1 ];
b11 = p[ j + by1 ]; sx = s_curve(rx0);
sy = s_curve(ry0); #define at2(rx,ry) ( rx * q[0] + ry * q[1] ) q = g2[ b00 ] ; u = at2(rx0,ry0);
q = g2[ b10 ] ; v = at2(rx1,ry0);
a = lerp(sx, u, v); q = g2[ b01 ] ; u = at2(rx0,ry1);
q = g2[ b11 ] ; v = at2(rx1,ry1);
b = lerp(sx, u, v); return lerp(sy, a, b);
} float noise3(float vec[])
{
int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
float rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
register int i, j; +--- 行: if (start) {------------------------------------------------------------ setup(, bx0,bx1, rx0,rx1);
setup(, by0,by1, ry0,ry1);
setup(, bz0,bz1, rz0,rz1); i = p[ bx0 ];
j = p[ bx1 ]; b00 = p[ i + by0 ];
b10 = p[ j + by0 ];
b01 = p[ i + by1 ];
b11 = p[ j + by1 ]; t = s_curve(rx0);
sy = s_curve(ry0);
sz = s_curve(rz0); #define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] ) q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
a = lerp(t, u, v); q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
b = lerp(t, u, v); c = lerp(sy, a, b); q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
a = lerp(t, u, v); q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
b = lerp(t, u, v); d = lerp(sy, a, b); return lerp(sz, c, d);
} static void normalize2(float v[])
{
float s; s = sqrt(v[] * v[] + v[] * v[]);
v[] = v[] / s;
v[] = v[] / s;
} static void normalize3(float v[])
{
float s; s = sqrt(v[] * v[] + v[] * v[] + v[] * v[]);
v[] = v[] / s;
v[] = v[] / s;
v[] = v[] / s;
} static void init(void)
{
int i, j, k; for (i = ; i < B ; i++) {
p[i] = i; g1[i] = (float)((random() % (B + B)) - B) / B; for (j = ; j < ; j++)
g2[i][j] = (float)((random() % (B + B)) - B) / B;
normalize2(g2[i]); for (j = ; j < ; j++)
g3[i][j] = (float)((random() % (B + B)) - B) / B;
normalize3(g3[i]);
} while (--i) {
k = p[i];
p[i] = p[j = random() % B];
p[j] = k;
} for (i = ; i < B + ; i++) {
p[B + i] = p[i];
g1[B + i] = g1[i];
for (j = ; j < ; j++)
g2[B + i][j] = g2[i][j];
for (j = ; j < ; j++)
g3[B + i][j] = g3[i][j];
}
}
顶点着色器:
#version core uniform mat4 MVMat;
uniform mat4 MVPMat;
uniform mat4 normalMat; uniform vec3 lightPos;
uniform float scale; layout(location = )in vec3 iPos;
layout(location = )in vec3 iNormal; out float lightIntensity;
out vec3 mcPos; void main()
{
vec3 ecPos = vec3(MVMat * vec4(iPos,));
mcPos = iPos * scale;
vec3 tnorm = normalize(vec3(normalMat * vec4(iNormal,1.0)));
vec3 lpos = vec3(MVMat * vec4(lightPos,1.0));
lightIntensity = dot(normalize(lpos - ecPos),tnorm);
lightIntensity *= 1.5f;
gl_Position = MVPMat * vec4(iPos,);
}
片元着色器:
#version core uniform sampler3D noise;
uniform vec3 skyColor;
uniform vec3 cloudColor; in float lightIntensity;
in vec3 mcPos; out vec4 color; void main()
{
vec4 noisevec = texture(noise,mcPos);
float intensity = (noisevec[] + noisevec[] + noisevec[] + noisevec[] + 0.03125) * 1.5; vec3 c = mix(skyColor,cloudColor,intensity) * lightIntensity;
color = vec4(c,1.0);
}
效果图:

实践源代码:https://github.com/xin-lover/opengl-learn/tree/master/chapter-12-procedural_texturing
Linux OpenGL 实践篇-12-procedural-texturing的更多相关文章
- Linux OpenGL 实践篇-6 光照
经典光照模型 经典光照模型通过单独计算光源成分得到综合光照效果,然后添加到物体表面特定点,这些成分包括:环境光.漫反射光.镜面光. 环境光:是指不是来特定方向的光,在经典光照模型中基本是个常量. 漫反 ...
- Linux OpenGL 实践篇-5 纹理
纹理 在之前的实践中,我们所渲染的物体的表面颜色都是纯色或者根据顶点位置计算出的一个颜色,这种方式在表现物体细节方面是比较吃资源的,因为我们每增加一个细节,我们就需要定义更多的顶点及其属性.所以美术人 ...
- Linux OpenGL 实践篇-4 坐标系统
OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...
- Linux OpenGL 实践篇-3 绘制三角形
本次实践是绘制两个三角形,重点理解顶点数组对象和OpenGL缓存的使用. 顶点数组对象 顶点数组对象负责管理一组顶点属性,顶点属性包括位置.法线.纹理坐标等. OpenGL缓存 OpenGL缓存实质上 ...
- Linux OpenGL 实践篇-2 创建一个窗口
OpenGL 作为一个图形接口,并没有包含窗口的相关内容,但OpenGL使用必须依赖窗口,即必须在窗口中绘制.这就要求我们必须了解一种窗口系统,但不同的操作系统提供的创建窗口的API都不相同,如果我们 ...
- Linux OpenGL 实践篇-1 OpenGL环境搭建
本次实践所使用环境为CentOS 7. 参考:http://www.xuebuyuan.com/1472808.html OpenGL开发环境搭建: 1.opengl库安装 opengl库使用mesa ...
- Linux OpenGL 实践篇-16 文本绘制
文本绘制 本文主要射击Freetype的入门理解和在OpenGL中实现文字的渲染. freetype freetype的官网,本文大部分内容参考https://www.freetype.org/fre ...
- Linux OpenGL 实践篇-15-图像数据操作
OpenGL图像数据操作 之前的实践中,我们在着色器中的输入输出都是比较固定的.比如在顶点或片元着色器中,顶点属性的输入和帧缓存的颜色值:虽然我们可以通过纹理或者纹理缓存对象(TBO)来读取任意的内存 ...
- Linux OpenGL 实践篇-14-多实例渲染
多实例渲染 OpenGL的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果 ...
随机推荐
- SSE2介绍及其简单用法举例
http://blog.csdn.net/fengbingchun/article/details/18515241
- MPTCP in Wireshark(转)
最新的wireshark可以直接识别出mptcp. Wireshark is a widely used network analyzer that can capture network traff ...
- 4-3逻辑非运算符及案例 & 4-4
创建类 LoginDemo3 这里取反 !(n%3==0) package com.imooc.operator; import java.util.Scanner; public class Log ...
- 【OpenJ_Bailian - 1328】Radar Installation (贪心)
Radar Installation 原文是English,直接上中文 Descriptions: 假定海岸线是无限长的直线.陆地位于海岸线的一侧,海洋位于另一侧.每个小岛是位于海洋中的一个点.对于任 ...
- AVAudioPlayer 如何在页面呈现之后按需初始化
在页面中按需初始化 AVAudioPlayer 很多时候我们需要根据页面上内容的情况创建 AVAudioPlayer 对象,已达到降低无谓资源占用等目的.下面我们来看一段代码看起来正确的代码: ove ...
- Mysql用户root密码找回
1.本试例的环境如下: 2.mysql数据库的版本如下,此数据库运行多实例: mysql Ver 15.1 Distrib 10.2.24-MariaDB, for Linux (x86_64) us ...
- Hexo搭建博客教程(1) - 安装环境与本地搭建
前言 搭建个人博客一般有两种选择,一个是使用WordPress,但是需要将博客搭建在服务器上,不过搭建好后写文章方便,适合没有程序基础的人使用.另一个是使用Hexo,相对简洁高效,不需要服务器,既可以 ...
- mvn从下载安装到纯命令行创建第一个mvn程序(编码,编译,测试,安装,打包)全过程细致分解
1.maven的下载和安装: a.maven的下载注意事项:如果你是windows,请选择①号,如果你是linux,请选择②号,下载地址:http://maven.apache.org/downloa ...
- C# 面向对象之继承
Object是所有类的基类 1.在C#中一个类可以继承另一个类(密封类除外,静态类是密封的不能被继承); 2.被继承的类被成为基类(父类);继承的类被成为派生类(子类); 3.子类将获得父类除构造函数 ...
- Django (九) 项目开发流程&项目架构
项目开发流程&项目架构 1. 软件开发的一般流程 1. 需求分析及确认: 由需求分析工程师与客户确认甚至挖掘需求.输出需求说明文档. 2. 概要设计及详细设计: 开发对需求进行概要设计,包 ...