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的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果 ...
随机推荐
- Walk of Length 6
简要题意: 给一n(n<=2000)个点的有标号无向图,在图上从1出发走六步回到1,问有多少种不是六元简单环的情况. 解法: 用暴力找到31种走法,环有9种形状: 分为9种,统计出每一种情况的方 ...
- Flutter实战视频-移动电商-31.列表页_列表切换交互制作
31.列表页_列表切换交互制作 博客地址:https://jspang.com/post/FlutterShop.html#toc-c42 点击左侧的大类右边的小类也跟着变化 新建provide 要改 ...
- 创建Python本地副本
创建本地副本后可以避免解释器找不到模块的情况. 1. 创建一个测试用的pl.py def printTest(): print("this is a test") 2. 将pl.p ...
- man帮助的使用
本篇博文转自Linux社区,谨在此记录使用: ——————Linux系统man帮助的使用技巧———————— man的查询后面的数字含义:1用户在shell换机中可以操作的指令或可执行文件2系统核心可 ...
- 洛谷 - P2551 - 华夏60战斗机 - 简单dp
https://www.luogu.org/problemnew/show/P2551 首先这道题没有给Hm的最大值,很坑,只能随便开一个100没想到还过了. 观察题目,发现虽然高度可以变化,但是速度 ...
- 洛谷 - P3164 - 和谐矩阵 - 高斯约旦消元法
为什么可以跑n立方,我也不知道,反正就是可以. 模2意义的,据说每一行可以存一个bitset,会比用bool更快(快32倍?). 本题告诉我们一个道理: 高斯消元之后,每个变量的含义不变(虽然交换了两 ...
- PhpStorm插件之Api Debugger
安装插件 File->Setting->Pluugins 搜索 Api Debugger 如何使用 安装完插件后,RESTART IDE,在编辑器右侧 即可找到最新安装的 Api D ...
- Lightoj 1129【字典树】
题意:如果存在一个串是另一个串的公共前缀就是NO,否则就是YES 思路:利用字典树的特性搞搞就好了 #include <bits/stdc++.h> using namespace std ...
- 51nod1267(双指针)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1267 题意:中文题诶- 思路:双指针 求a+b+c+d=0,令 ...
- bootstrap添加多个模态对话框支持
bootstrap添加多个模态对话框支持 (2015-03-04 21:05:35) 转载▼ 标签: 房产 因为项目需要,在页面交互上要弹出多个dialog窗口,而bootstrap的modal支 ...