OpenGL(十五)雾效 的 shader 实现

2018年1月29日 没有评论

雾效 可以表现出一个线性的颜色变化,常用于烘托场景气氛。这种效果的实现原理并不复杂,本文着重讨论如何通过shader编写完成雾效的实现。

核心原理

雾效 的核心在于:计算视点与物体之间的距离,然后根据一个衰减公式进行颜色混合。常规的算法有线性衰减,指数衰减,指数幂衰减。

线性雾效

使用线性差值衰减,即可达到这种 雾效 。示意图如下:

OpenGL(十五)雾效 的 shader 实现

对应shader如下:

//vs
varying vec3 V_Normal;
varying vec3 V_WorldPos;
varying vec4 V_EyeSpacePos;
void main()
{
    V_Normal=mat3(NM)*normal;
    vec4 worldPos=M*vec4(pos,1.0);
    V_WorldPos=worldPos.xyz;
    //将物体换算到视口坐标系
    V_EyeSpacePos=V*worldPos;
    gl_Position=P*V*worldPos;
}
//fs
uniform float U_FogStart;
uniform float U_FogEnd;
uniform vec4 U_FogColor;
varying vec3 V_Normal;
varying vec3 V_WorldPos;
varying vec4 V_EyeSpacePos;
float LinearFog(float distance)
{
    float fogAlpha=(distance-U_FogStart)/(U_FogEnd-U_FogStart);
    fogAlpha=clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}
void main()
{
    //color calculate
    ...
    //
    float fogAlpha = LinearFog(abs(V_EyeSpacePos.z/V_EyeSpacePos.w));
    gl_FragColor=mix(color,U_FogColor,fogAlpha);
}

指数雾效

指数雾效与上面的shader大体相同,唯一要更改的是衰减算法。添加一个density参数来控制曲线的弧度。

//fs
uniform vec4 U_Density;
float ExpFog(float distance)
{
    float fogAlpha=exp(-distance * U_Density);
    fogAlpha=1.0-clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}

指数幂雾效

通过Gradiant可以控制雾效的衰减速度。

//fs
uniform vec4 U_Density;
uniform vec4 U_Gradiant;
float ExpFog(float distance)
{
    float fogAlpha=exp(-pow(distance * U_Density,UGradiant));
    fogAlpha=1.0-clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}

总结

本文通过三种shader实现,介绍了如何制作 雾效 来烘托氛围效果。

分类: 未分类 标签:

OpenGL(十五)雾效 的 shader 实现

2018年1月29日 没有评论

雾效 可以表现出一个线性的颜色变化,常用于烘托场景气氛。这种效果的实现原理并不复杂,本文着重讨论如何通过shader编写完成雾效的实现。

核心原理

雾效 的核心在于:计算视点与物体之间的距离,然后根据一个衰减公式进行颜色混合。常规的算法有线性衰减,指数衰减,指数幂衰减。

线性雾效

使用线性差值衰减,即可达到这种 雾效 。示意图如下:

OpenGL(十五)雾效 的 shader 实现

对应shader如下:

//vs
varying vec3 V_Normal;
varying vec3 V_WorldPos;
varying vec4 V_EyeSpacePos;
void main()
{
    V_Normal=mat3(NM)*normal;
    vec4 worldPos=M*vec4(pos,1.0);
    V_WorldPos=worldPos.xyz;
    //将物体换算到视口坐标系
    V_EyeSpacePos=V*worldPos;
    gl_Position=P*V*worldPos;
}
//fs
uniform float U_FogStart;
uniform float U_FogEnd;
uniform vec4 U_FogColor;
varying vec3 V_Normal;
varying vec3 V_WorldPos;
varying vec4 V_EyeSpacePos;
float LinearFog(float distance)
{
    float fogAlpha=(distance-U_FogStart)/(U_FogEnd-U_FogStart);
    fogAlpha=clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}
void main()
{
    //color calculate
    ...
    //
    float fogAlpha = LinearFog(abs(V_EyeSpacePos.z/V_EyeSpacePos.w));
    gl_FragColor=mix(color,U_FogColor,fogAlpha);
}

指数雾效

指数雾效与上面的shader大体相同,唯一要更改的是衰减算法。添加一个density参数来控制曲线的弧度。

//fs
uniform vec4 U_Density;
float ExpFog(float distance)
{
    float fogAlpha=exp(-distance * U_Density);
    fogAlpha=1.0-clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}

指数幂雾效

通过Gradiant可以控制雾效的衰减速度。

//fs
uniform vec4 U_Density;
uniform vec4 U_Gradiant;
float ExpFog(float distance)
{
    float fogAlpha=exp(-pow(distance * U_Density,UGradiant));
    fogAlpha=1.0-clamp(fogAlpha,0.0,1.0);
    return fogAlpha;
}

总结

本文通过三种shader实现,介绍了如何制作 雾效 来烘托氛围效果。

分类: 未分类 标签:

OpenGL(十四)环境反射 环境折射 的shader实现

2018年1月22日 没有评论

在上一节我们讲解了如何制作天空盒,本篇文章介绍它的一个衍生功能——在物体上叠加 环境反射 。

核心原理

核心很简单,在天空盒中,视线照射到天空盒上的区域即需要绘制的部分。在环境反射中,需要绘制的区域变成了视线在物体表面的反射光线,反射光线照射到天空盒的位置即要绘制的部分。

环境反射

按照上面的说法,将法线方向传入shader中,具体编写如下:

//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;
uniform mat4 M;
uniform mat4 P;
uniform mat4 V;
uniform mat4 NM;
varying vec4 V_WorldPos;
varying vec3 V_Normal;
void main()
{
    V_WorldPos=M*vec4(pos,1.0);
    V_Normal=mat3(NM)*normal;
    gl_Position=P*V*M*vec4(pos,1.0);
}
//fs
varying vec4 V_WorldPos;
varying vec3 V_Normal;
uniform samplerCube U_MainTexture;
void main()
{
    vec3 eyeVec=normalize(V_WorldPos.xyz-vec3(0.0));
    vec3 n=normalize(V_Normal);
    vec3 r=reflect(eyeVec,n);
    vec4 color=textureCube(U_MainTexture,r);
    gl_FragColor=color;
}

环境折射

环境折射实现原理与环境反射相同,只不过在计算射出光线时使用折射函数:

//fs
varying vec4 V_WorldPos;
varying vec3 V_Normal;
uniform samplerCube U_MainTexture;
void main()
{
    vec3 eyeVec=normalize(V_WorldPos.xyz-vec3(0.0));
    vec3 n=normalize(V_Normal);
    vec3 r=refract(eyeVec,n,1.0/2.4);
    vec4 color=textureCube(U_MainTexture,r);
    gl_FragColor=color;
}

总结

借助天空盒,可以很方便的实现环境反射和环境折射的效果。其中折射率有对照表,可以通过搜索引擎查到。

OpenGL(十四)环境反射 环境折射 的shader实现

转载请注明出处:http://blog.csdn.net/fansongy/article/details/79128108

分类: 未分类 标签:

OpenGL(十四)环境反射 环境折射 的shader实现

2018年1月22日 没有评论

在上一节我们讲解了如何制作天空盒,本篇文章介绍它的一个衍生功能——在物体上叠加 环境反射 。

核心原理

核心很简单,在天空盒中,视线照射到天空盒上的区域即需要绘制的部分。在环境反射中,需要绘制的区域变成了视线在物体表面的反射光线,反射光线照射到天空盒的位置即要绘制的部分。

环境反射

按照上面的说法,将法线方向传入shader中,具体编写如下:

//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;
uniform mat4 M;
uniform mat4 P;
uniform mat4 V;
uniform mat4 NM;
varying vec4 V_WorldPos;
varying vec3 V_Normal;
void main()
{
    V_WorldPos=M*vec4(pos,1.0);
    V_Normal=mat3(NM)*normal;
    gl_Position=P*V*M*vec4(pos,1.0);
}
//fs
varying vec4 V_WorldPos;
varying vec3 V_Normal;
uniform samplerCube U_MainTexture;
void main()
{
    vec3 eyeVec=normalize(V_WorldPos.xyz-vec3(0.0));
    vec3 n=normalize(V_Normal);
    vec3 r=reflect(eyeVec,n);
    vec4 color=textureCube(U_MainTexture,r);
    gl_FragColor=color;
}

环境折射

环境折射实现原理与环境反射相同,只不过在计算射出光线时使用折射函数:

//fs
varying vec4 V_WorldPos;
varying vec3 V_Normal;
uniform samplerCube U_MainTexture;
void main()
{
    vec3 eyeVec=normalize(V_WorldPos.xyz-vec3(0.0));
    vec3 n=normalize(V_Normal);
    vec3 r=refract(eyeVec,n,1.0/2.4);
    vec4 color=textureCube(U_MainTexture,r);
    gl_FragColor=color;
}

总结

借助天空盒,可以很方便的实现环境反射和环境折射的效果。其中折射率有对照表,可以通过搜索引擎查到。

OpenGL(十四)环境反射 环境折射 的shader实现

转载请注明出处:http://blog.csdn.net/fansongy/article/details/79128108

分类: 未分类 标签:

iOS11带来的技术变化注意事项

2017年9月26日 没有评论

相册权限

iOS11以前:
NSPhotoLibraryUsageDescription:访问相册和存储照片到相册(读写),会出现用户授权。

iOS11之后:
NSPhotoLibraryUsageDescription:无需添加。默认开启访问相册权限(读),无需用户授权。
NSPhotoLibraryAddUsageDescription: 添加内容到相册。(写),会出现用户授权。

分类: 未分类 标签:

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

2017年9月20日 没有评论

有时候我们会被系统层诡异的Bug绊翻了车,不得已的情况下,需要更改Android代码。有些类我们能拿到源码,一切好说,但更多情况下,是引擎内或SDK中的jar文件,我们无法获得源码。面对这种情况,我的思路是:

  1. 解开Apk文件
  2. 找到对应到.class文件
  3. 定位更改代码位置
  4. 更改smali文件或代码
  5. 编回Apk
  6. 签名文件

流程中规中矩,具体操作如下:

ApkTool解包

首先在ApkTool的官网上下载安装工具,地址为https://ibotpeaches.github.io/Apktool/。安装完成后运行命令:

apktool d xxx.apk

运行后,会在apk的同级目录创建名称相同的文件夹,里面有解包之后的文件。目录结构如下:

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

其中smali为对应的可更改文件,build/apk/classes.dex为所有的代码文件。

dex2jar转换代码

由于smali代码没有可读性,我们需要将dex文件转换成jar文件。在官网https://sourceforge.net/projects/dex2jar/上下载工具,解压后,将上面提到的classes.dex复制到dex2jar的目录下,进入到这个路径下,运行命令行:

d2j-dex2jar.bat classes.dex

会生成名为calsses-dex2jar.jar的文件。

jd-gui分析代码

在jd-gui的官网http://jd.benow.ca/下载这个工具,双击运行后,将刚刚生成的jar文件拖动进去,即可看到java文件。

在分析过代码之后,我们希望更改内容或者输出日志,就需要找到smali文件夹中的对应文件。

更改smali

手动改smali就是硬功夫了,大家可以在网上搜搜smali的语法,然后直接去改文件。如果做过破解相信大家很熟悉这种手改汇编的痛苦。这里就不展开吐槽了。举个小栗子:

Log.e("FSY",obj.toString());

这行代码转到smali代码,就是下面这一坨:

invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String; 

move-result-object v1

const-string v2, "FSY"
    
invoke-static {v2, v1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

具体语法可以参考这篇文章http://blog.csdn.net/wdaming1986/article/details/8299996以及它后面的引用链接。

如果你实在想直接改Java代码,可以创建一个Java工程,把jar包扔进去,改了代码之后,再把对应的smali文件检出来。理论上是可行,不过我不建议尝试,因为通常都会缺少依赖项编译失败。

编译回Apk

改过smali文件之后,运行apktool的命令可以把包打回去

apktool b path/to/unzip/file -o songyangApp.apk

运行之后,就可以生成新包了。

签名

最后如果希望包可以运行还需要重新签名。运行命令

jarsigner -verbose -keystore yourKey.keystore -storepass yourPassword path/to/apk /path/to/keystore/file

即可完成签名。

总结

如果看了上面的这套复杂的流程,你还是要坚持这样查问题,那你可能和我一样走投无路,希望上面的文字对你有帮助,祝你好运。

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

2017年9月20日 没有评论

有时候我们会被系统层诡异的Bug绊翻了车,不得已的情况下,需要更改Android代码。有些类我们能拿到源码,一切好说,但更多情况下,是引擎内或SDK中的jar文件,我们无法获得源码。面对这种情况,我的思路是:

  1. 解开Apk文件
  2. 找到对应到.class文件
  3. 定位更改代码位置
  4. 更改smali文件或代码
  5. 编回Apk
  6. 签名文件

流程中规中矩,具体操作如下:

ApkTool解包

首先在ApkTool的官网上下载安装工具,地址为https://ibotpeaches.github.io/Apktool/。安装完成后运行命令:

apktool d xxx.apk

运行后,会在apk的同级目录创建名称相同的文件夹,里面有解包之后的文件。目录结构如下:

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

其中smali为对应的可更改文件,build/apk/classes.dex为所有的代码文件。

dex2jar转换代码

由于smali代码没有可读性,我们需要将dex文件转换成jar文件。在官网https://sourceforge.net/projects/dex2jar/上下载工具,解压后,将上面提到的classes.dex复制到dex2jar的目录下,进入到这个路径下,运行命令行:

d2j-dex2jar.bat classes.dex

会生成名为calsses-dex2jar.jar的文件。

jd-gui分析代码

在jd-gui的官网http://jd.benow.ca/下载这个工具,双击运行后,将刚刚生成的jar文件拖动进去,即可看到java文件。

在分析过代码之后,我们希望更改内容或者输出日志,就需要找到smali文件夹中的对应文件。

更改smali

手动改smali就是硬功夫了,大家可以在网上搜搜smali的语法,然后直接去改文件。如果做过破解相信大家很熟悉这种手改汇编的痛苦。这里就不展开吐槽了。举个小栗子:

Log.e("FSY",obj.toString());

这行代码转到smali代码,就是下面这一坨:

invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String; 

move-result-object v1

const-string v2, "FSY"
    
invoke-static {v2, v1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

具体语法可以参考这篇文章http://blog.csdn.net/wdaming1986/article/details/8299996以及它后面的引用链接。

如果你实在想直接改Java代码,可以创建一个Java工程,把jar包扔进去,改了代码之后,再把对应的smali文件检出来。理论上是可行,不过我不建议尝试,因为通常都会缺少依赖项编译失败。

编译回Apk

改过smali文件之后,运行apktool的命令可以把包打回去

apktool b path/to/unzip/file -o songyangApp.apk

运行之后,就可以生成新包了。

签名

最后如果希望包可以运行还需要重新签名。运行命令

jarsigner -verbose -keystore yourKey.keystore -storepass yourPassword path/to/apk /path/to/keystore/file

即可完成签名。

总结

如果看了上面的这套复杂的流程,你还是要坚持这样查问题,那你可能和我一样走投无路,希望上面的文字对你有帮助,祝你好运。

Unit3d开发 (二十一) 更改Apk的smali 排查Java层问题

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

解决UITableView xib添加到Storyboard出现IB Designables错误

2017年8月3日 没有评论

之前提过 Swift下自定义xib添加到Storyboard 的方法。最近有人问说按照文中方法会出现IBDesignables错误,导致在xcode Storyboard中无法显示。

这个应该是我漏讲了。如果你的自定义xib中有UITableView,而且UITableViewCell也是xib,一般这个错误肯定是加载的时候找不到对应的Bundle文件了。

错误如下:
解决UITableView xib添加到Storyboard出现IB Designables错误

IB Designables: Failed to render and update auto layout status for ViewController (BYZ-38-t0r): The agent threw an exception.
IB Designables: Failed to update auto layout status: The agent raised a
"NSInternalInconsistencyException" exception: Could not load NIB in bundle:
'NSBundle </Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays> (loaded)' with name 'MyTableViewCell'

为什么会出现这个错误?

这个就要说到ios xib(Storyborad)加载机制了,但apple把这个Bundle加载机制隐藏的很深,之后我会单独写一篇文章介绍xib(Storyborad)加载机制。这里我简单说下上面的错误是怎么导致的。

上面错误主要因为我们使用的了自定义的MyTableViewCell(Xib),在Storyboard渲染Build的时候,会去读取加载这个MyTableViewCell(Xib)。这个时候我们代码一般是这么写的:

tableView.register(UINib(nibName: "MyTableViewCell", bundle: nil),
forCellReuseIdentifier: "MyTableViewCell")

注意这一句:UINib(nibName: "MyTableViewCell", bundle: nil),这个在App运行起来没有问题,因为bundle = nil 则默认使用mainBundle,但是xcode中预览Storyboard的时候,我们App没有运行,所以根据上下文无法找到对应的Bundle,所以导致该Nib无法加载。
所以出现了上面的错误exception: Could not load NIB in bundle


那么如何修改呢?

很简单,既然根据上下文无法找到对应的Bundle,我们告诉它不就行了。改成:

tableView.register(UINib(nibName: "MyTableViewCell",
        bundle: Bundle(for: self.classForCoder)),
        forCellReuseIdentifier: "MyTableViewCell")

你也可以加载TARGET_INTERFACE_BUILDER来区分下。当然不区分也没关系。

#if TARGET_INTERFACE_BUILDER
    tableView.register(UINib(nibName: "MyTableViewCell",
        bundle: Bundle(for: self.classForCoder)),
        forCellReuseIdentifier: "MyTableViewCell")
#else
    tableView.register(UINib(nibName: "MyTableViewCell",
        bundle: nil), 
        forCellReuseIdentifier: "MyTableViewCell")
#endif

我非常欢迎大家给我提问题(让人民群众监督我学习哈哈哈~~~~, 这不我就得重新理下Nib加载机制)

分类: 未分类 标签:

[置顶] 房卡麻将分析之“缺人玩法”

2017年6月21日 没有评论

         一般玩麻将都是四个人,遇到 “三缺一”怎么办?大多数情况下,玩家只好等待或叫人,这无疑增加了玩家的等待时间,同时也减缓了房卡的消耗速度。其实四个人并不是玩麻将的硬性条件,如果能在游戏中不受四人限制随时开局,就会大大提升房卡麻将的实用性和可玩性,同时有效的加快房卡销售速度。


         所谓”缺人玩法“,即在原有基础上提供二人,三人的玩法,如果玩家有两个人在房间里开局,那就形成南北对立局势进行游戏。

[置顶]        房卡麻将分析之“缺人玩法”


         如果玩家有三个人在房间里开局,那就形成去掉对门,左右代表上家和下家,形成三国鼎力之势。

[置顶]        房卡麻将分析之“缺人玩法”

         在开发过程中,本身的技术难度并不大,主要是要注意表现层的细节,总体来说有以下几个方面:

   

         (1)。玩家坐下后所在方向的处理:二人麻将比较好处理,房间两个玩家,自身处下方位无须处理,对方玩家需要放置在对门。要注意东南西北方面显示和切换的细节。可以根据服务器分配的座位号进行重新的整理。三人麻将的话,要注意一个地方,就是如果三个玩家的手机上显示的都是自已居下位,左边为上家,右边为下家,座位与方位的不统一,这是很奇怪的一个现象,但又的确存在。即如果玩家A是南,玩家B是东,玩家C是西,这时就会出现B玩家,C玩家本应显示为对门但确不是的情况,哈哈,但为了美观,这里可以不太讲究。毕竟不影响胜负和打牌顺序。

[置顶]        房卡麻将分析之“缺人玩法”

                                          《大赢家》麻将的三人开局


[置顶]        房卡麻将分析之“缺人玩法”


         (2)。玩家出牌的排列处理:因为二人,三人玩的情况下,每个人摸到的牌的数量会多出来一些,在桌面上显示时就会遇到出牌牌墙过长的问题,这里要做一下对齐或增加行数的问题。


[置顶]        房卡麻将分析之“缺人玩法”

                                   二人麻将,要增长出牌的牌墙


[置顶]        房卡麻将分析之“缺人玩法”

                              三人麻将,三人出牌的牌墙不要交叉。


         (3)。玩家结算显示处理:这个就比较简单了,玩家结算时能够同态的根据人数进行界面结算框显示,保证美观。


[置顶]        房卡麻将分析之“缺人玩法”

           服务器方面,要改进的地方并不是很多,在房间第一次开局时记录下人数,并以这个人数来进行后续开局判断,起牌顺序处理和结算就行了,整体改动比较简单。

        地方棋牌,VR,AR技术,请关注公众号:"红孩儿的游戏开发之路“

               红孩儿微信:honghaier_game


          [置顶]        房卡麻将分析之“缺人玩法”

分类: 未分类 标签:

OpenGL(十三) 天空盒 的 shader 实现

2017年6月19日 没有评论

天空盒 的使用可以使用户感觉所处一个封闭的世界环境,使得游戏更加真实。本文主要介绍如何通过OpenGL和GLSL来实现天空盒。

核心原理

天空盒 的核心原理其实并不复杂,它相当于在视线外套上一个盒子。盒子有6个面,分别使用6张可以拼接在一起的图片。从原理上讲,通过比较视线的xyz,可以找到方向,可以计算出应该渲染的与盒子面的交点,进而选出应绘制的面进行渲染。从实现的角度理解,首先将天空盒与摄像机放在同一位置,然后将盒子绘制出来即可。

实现

C++代码方面,通过SOIL可以创建一个cubemap,然后将其传入shader,其接方法如下:

//Create
GLuint mainTexture = SOIL_load_OGL_cubemap(
        "res/image/right.bmp",
        "res/image/left.bmp", 
        "res/image/top.bmp", 
        "res/image/bottom.bmp", 
        "res/image/back.bmp", 
        "res/image/front.bmp",
        0, 0, SOIL_FLAG_POWER_OF_TWO);

//Draw
glBindTexture(GL_TEXTURE_CUBE_MAP, mainTexture);
glUniform1i(originalProgram.GetLocation("U_MainTexture"),0);
cube.Bind(originalProgram.GetLocation("pos"), originalProgram.GetLocation("texcoord"), originalProgram.GetLocation("normal"));
cube.Draw();

着色器方面,接收传入的值后,将3D采样纹理绘制出即可。实现shader为:

//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;

uniform mat4 M;
uniform mat4 P;
uniform mat4 V;

varying vec3 V_Texcoord;
void main()
{
    V_Texcoord=pos;
    gl_Position=P*V*M*vec4(pos,1.0);
}

//fs
varying vec3 V_Texcoord;

uniform samplerCube U_MainTexture;

void main()
{
    gl_FragColor=textureCube(U_MainTexture,V_Texcoord);
}

总结

通过上面的代码即可在OpenGL中实现 天空盒 的效果。

OpenGL(十三) 天空盒 的 shader 实现

OpenGL(十三) 天空盒 的 shader 实现

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签: