Linux OpenGL 实践篇-12-ProceduralTexturing

程序式纹理

  简单的来说程序式纹理就是用数学公式描述物体表面的纹路 。而实现这个过程的着色器我们称之为程序纹理着色器,通常在这类着色器中我们能使用的输入信息也就是顶点坐标和纹理坐标。

程序式纹理的优点

1.程序式纹理的内存占用比预存纹理要低的多;因为程序式纹理主要是算法的实现,数据都是通过计算产生的;

2.程序生成的纹理没有固定的面积和分辨率,可以随意的应用到不同大小的物体,而不用担心精度不够的问题;

3.程序式纹理可以写入一些算法的关键参数,可以方便的供程序修改从而创建出有趣的效果,而预存的纹理则很难进行改动;

4.如果我们使用程序式纹理计算体积而不是表面的话,那么体积的剖面表现力会比任何使用2维纹理的方式要真实的多。

程序式纹理缺点

1.程序式纹理对编程人员的要求较高,特别是算法要求;

2.程序式纹理的数据是实时计算产生的,所以每一次都需要重新计算,相对预存纹理时间会花费的比较多;

3.程序式纹理可能会带来一些难以克服的走样问题;而对于预存纹理现在的图形硬件都有比较成熟的反走样算法;

4.由于数学精度上的差异和噪声实现算法上的差异,在不同平台上程序式纹理的表现不一定一致。

简单的程序式纹理

砖块

顶点着色器:

#version 330 core                                           

layout(location=0) in vec3 iPos;
layout(location=1) 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 330 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 330 core

layout(location=0) in vec3 iPos;
layout(location=1) 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 330 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(1,0,0,1);
}

效果图:

噪声

  使用计算机渲染精致的物体式非常的容易的,但在真实的世界中物体往往不是这样的,它们经常是带污渍、凹痕、磨损的,如果要实现这样的效果,艺术家通常要花费很多的时间来进行构建。针对这个问题,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 + 2];
static float g3[B + B + 2][3];
static float g2[B + B + 2][2];
static float g1[B + B + 2];
static int start = 1;

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+1) & BM;r0 = t - (int)t;r1 = r0 - 1.;

double noise1(double arg)
{
        int bx0, bx1;
        float rx0, rx1, sx, t, u, v, vec[1];

        vec[0] = arg;
+---  4 行: if (start) {------------------------------------------------

        setup(0, 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[2])
{
        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;

+---  4 行: if (start) {----------------------------------------------------------------------

        setup(0, bx0,bx1, rx0,rx1);
        setup(1, 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[3])
{
        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;

+---  4 行: if (start) {------------------------------------------------------------

        setup(0, bx0,bx1, rx0,rx1);
        setup(1, by0,by1, ry0,ry1);
        setup(2, 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[2])
{
        float s;

        s = sqrt(v[0] * v[0] + v[1] * v[1]);
        v[0] = v[0] / s;
        v[1] = v[1] / s;
}

static void normalize3(float v[3])
{
        float s;

        s = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
        v[0] = v[0] / s;
        v[1] = v[1] / s;
        v[2] = v[2] / s;
}

static void init(void)
{
        int i, j, k;

        for (i = 0 ; i < B ; i++) {
                p[i] = i;

                g1[i] = (float)((random() % (B + B)) - B) / B;

                for (j = 0 ; j < 2 ; j++)
                        g2[i][j] = (float)((random() % (B + B)) - B) / B;
                normalize2(g2[i]);

                for (j = 0 ; j < 3 ; 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 = 0 ; i < B + 2 ; i++) {
                p[B + i] = p[i];
                g1[B + i] = g1[i];
                for (j = 0 ; j < 2 ; j++)
                        g2[B + i][j] = g2[i][j];
                for (j = 0 ; j < 3 ; j++)
                        g3[B + i][j] = g3[i][j];
        }
}

顶点着色器:

#version 330 core

uniform mat4 MVMat;
uniform mat4 MVPMat;
uniform mat4 normalMat;

uniform vec3 lightPos;
uniform float scale;

layout(location = 0)in vec3 iPos;
layout(location = 2)in vec3 iNormal;

out float lightIntensity;
out vec3 mcPos;

void main()
{
        vec3 ecPos = vec3(MVMat * vec4(iPos,1));
        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,1);
}

片元着色器:

#version 330 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[0] + noisevec[1] + noisevec[2] + noisevec[3] + 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

原文地址:https://www.cnblogs.com/xin-lover/p/9091129.html

时间: 2024-10-08 10:28:44

Linux OpenGL 实践篇-12-ProceduralTexturing的相关文章

Linux OpenGL 实践篇-1

本次实践所使用环境为CentOS 7. 参考:http://www.xuebuyuan.com/1472808.html OpenGL开发环境搭建: 1.opengl库安装 opengl库使用mesa库,安装命令: yum intall mesa* mesa库是一个开源的三维计算机图形库,以开源的形式实现了opengl应用程序接口.具体介绍:https://www.mesa3d.org/intro.html. 2.glut安装 下载freeglut,下载地址为: https://github.c

Linux OpenGL 实践篇-3 framebuffer

GLEW说明 GLEW(OpenGL Extension Wrangler) 是OpenGL的另一个辅助库,主要封装了从OpenGL库中获取函数地址的过程,还包含了一些可以跨平台使用的OpenGL编程方法. 本次实践是使用数据缓存绘制两个三角形,重点是缓存的创建和数据输入.数据输入后,根据数据使用方式可分为非基于索引绘制和基于索引绘制,使用的方法分别为glDrawArray和glDrayElements. 首先,明确OpenGL缓存使用步骤: glGenBuffer glBindBuffer g

Linux OpenGL 实践篇-2 创建一个窗口

OpenGL 作为一个图形接口,并没有包含窗口的相关内容,但OpenGL使用必须依赖窗口,即必须在窗口中绘制.这就要求我们必须了解一种窗口系统,但不同的操作系统提供的创建窗口的API都不相同,如果我们在学习OpenGL时要去学习一整套的窗口系统,这将带来很多的不便,所以出现了GLUT.GLUT全称OpenGL Utility Toolkit,是一套和窗口系统无关的软件包,为我们提供了窗口创建,用户输入输出处理等功能.优点是:简小,精悍.注意GLUT并不是一个功能特别全面的窗口系统工具包,所以构建

Linux OpenGL 实践篇-6 光照

经典光照模型 经典光照模型通过单独计算光源成分得到综合光照效果,然后添加到物体表面特定点,这些成分包括:环境光.漫反射光.镜面光. 环境光:是指不是来特定方向的光,在经典光照模型中基本是个常量. 漫反射光:是散射在各个方向上均匀的表面特定光源.物体表面通过光照照亮,即使这个表面没有将光源直接反射到你的眼睛中.漫反射与眼睛的方向没有关系,但与光源的方向有关,当表面直接面向光源的时候会表现的亮一些,而倾斜的时候则暗一些,因为在现实中倾斜的表面接受的光要少一些.在经典光照模型中,我们使用表面的法向量来

Linux OpenGL 实践篇-9 模型

之前一直渲染箱子,显得有点单调.这一次我们绘制一个用艺术家事先用建模工具创建的模型. 本次实践参考:https://learnopengl-cn.github.io/03%20Model%20Loading/01%20Assimp/ 在之前我们的OpenGL实践中,绘制图形的过程是先定义顶点的位置.法线.纹理坐标(UV)等信息,按一定的规则组织后传给着色器处理,最终绘制到屏幕上.现在使用艺术家构建的模型,绘制的过程并没有变,只不过顶点和使用的贴图信息从原来我们自己定义变为从已构建好的模型中提取,

Linux OpenGL 实践篇-14-多实例渲染

多实例渲染 OpenGL的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果我们使用常规的做法,渲染的过程应该是是:绘制第一个星球glBindVertexArray--glDrawArrays或glDrawElements,然后使用同样的流程绘制其它的星球.但这种方式非常容易达到计算机的性能瓶颈,就算是渲染的物体是最简单的面片,因为在绘制的整个过程中,绘制物体的时间其实非常

Linux网络服务12——NFS共享服务

Linux网络服务12--NFS共享服务 一.NFS简介 端口号:TCP.UDP 111端口 NFS(Network File System)网络文件系统,是一种基于TCP/IP传输的网络文件系统协议,最初由SUM公司开发.通过使用NFS协议,客户机可以像访问本地目录一样访问远程服务器中的共享资源. NFS没有用户认证机制,而且数据在网络上明文传输,所以安全性很差,一般只能在局域网中使用.NFS服务的实现依赖于RPC(Remote Process Call)远程过程调用机制,以完成远程到本地的映

SUSE Linux Enterprise Serve 12 试用体验

大家都知道德国出产的奔驰.宝马.等车型以精美.可靠.耐用而著称,而同样出自德国人之手的Suse Linux ,即使是被收购也是一款出色的Linux发行版,目前Suse成为唯一一家为IBM大型主机提供具有企业就绪和商业支持特性的Linux操作系统的公司, 特别针对IBM System z 架构进行了优化,使其安装过程非常简便.而且在Suse平台上面部署SAP和Oracle应用非常方便.在今年10月28日,Suse Enterprise Server 12(简称SLES 12)在11版发布4年后,经

在 Linux Mint 安装 Linux Kernel 4.12(稳定版)

Linus Torvalds 发布了 Linux 内核 4.12.你可以从这里直接下载相关的 deb 包来安装.或者,继续阅读本文,按下面的步骤安装新内核. 警告:Linux 内核是系统的关键元素.在某个硬件设备不正常工作时,可以尝试执行升级,新的内核可能会解决此问题. 但同样的,非必须地更新一个新的内核也可能导致不必要的回滚,例如,无网络连接, 没有声音,甚至是无法正常启动系统,所以安装一个新的内核,请正确认识风险. 最简单的安装任意内核方法 - 在Linux Mint 使用 UKUU. [e