Mac OpenGL踩坑记

在Mac上尝试写OpenGL程序,结果踩了一堆坑,在此记录下来,以备后用。

Mac上已经带了GLUT,使用时只需#include <GLUT/GLUT.h>即可。但使用GLUT函数编译时,Xcode会警告相关函数已经在OS X 10.9中废弃:

‘glutInit’ is deprecated: first deprecated in OS X 10.9

消除该警告的方法是在Build Settings中将OS X Deployment Target改为OS X 10.8,但这显然不是Apple认可的解决方案,更好的方案是使用GLFW和GLEW替代GLUT。

http://stackoverflow.com/questions/24095931/glut-deprecation-in-mac-osx-10-9-ide-qt-creator

使用GLFW和GLEW的问题是OS X默认并没有带上这两个库,需要我们自己安装。

从GLFW官网上下载源码,使用CMake编译:

http://www.glfw.org/docs/latest/compile.html#compile_compile

编译出的静态库路径在用CMake新建的工程文件夹下:

glfw-3.1.2/src/Debug/libglfw3.a

#include头文件路径在源码文件夹下:

glfw-3.1.2/include/GLFW/glfw3.h

在Build Settings中设置好Header Search Paths和Library Search Paths,在Build Phases的Link Binary With Libraries将libglfw2.a设置好,此时编译Xcode会报很多Undefined symbols的错误:

Undefined symbols for architecture x86_64:

“_CFArrayAppendValue”, referenced from:

__glfwInitJoysticks in libglfw3.a(iokit_joystick.o)

_addJoystickElement in libglfw3.a(iokit_joystick.o)

这是因为libglfw3.a在Mac OS X上使用了Cocoa,依赖了几个其它的库:Cocoa、IOKit和CoreVideo。将这3个库也在Link Binary With Libraries中设置好,编译就能通过了。

http://stackoverflow.com/questions/18391487/compiling-with-glfw3-linker-errors-undefined-reference

使用GLEW先从它的官网上下载源码,然后依次使用3个make命令编译安装:

1、make,编译。

2、sudo make install,安装,将头文件和库文件拷贝到系统头文件和库文件存放目录。

3、make clean,清除编译安装过程中留下来的垃圾文件。

https://github.com/nigels-com/glew

在Mac OS X EI Capitan上sudo make install时遇到了以下问题:

install -d -m 0755 “/usr/include/GL”

install: mkdir /usr/include: Operation not permitted

make: *** [install.include] Error 71

这是因为从EI Capitan开始,Mac OS X启用了System Integrity Protection,不再允许直接操作/usr下的目录,但/usr/local除外,所以需要将GLEW安装在/usr/local下。这里只需要修改GLEW的Makefile即可,将GLEW_DEST ?= /usr修改为GLEW_DEST ?= /usr/local,再执行sudo make install。

http://stackoverflow.com/questions/33309005/sudo-mkdir-in-usr-operation-not-permitted-el-capitan

同样需要配置好Header Search Paths、Library Search Paths和Link Binary With Libraries。此时编译报错:

/usr/local/include/GL/glew.h:16426:17: Declaration of ‘PFNGLCOPYTEXSUBIMAGE3DPROC’ must be imported from module ‘OpenGL.GL3’ before it is required

这里需要将Build Settings里的Enable Modules(C and Objective-C)设为No即可。最后别忘了Link OpenGL库本身。

C#与C++混淆点梳理

从C++到C#,发现C#写多了里面有些知识和C++混淆了,记下来理一理。
1、C++的类定义不会在class关键字前使用访问修饰符,即不会有public class Base {…}这种定义,而C#有。
C#的访问修饰符除了用于修饰member外还用于修饰type,其访问修饰符除了有和C++一样的public、protected和private外,还有internal和protected internal。
public:访问不受限制。
protected:访问仅限于包含类和包含类的派生类。
private:访问仅限于包含类。
internal:访问仅限于当前程序集。
protected internal:访问仅限于当前程序集和包含类的派生类。

修饰type时,如果是top-level types(没有被其它类型嵌套包含)只能使用public和internal,默认是internal。
如果是nested types则访问修饰符的情况有以下情况:
class默认修饰符是private,可以使用public、protected、private、internal、protected internal。
interface默认修饰符是public,只能使用public。
struct默认修饰符是private,可以使用public、private、internal。
enum默认修饰符是public,只能使用public。

当定义一个类型时需要考虑它的可访问级别是否依赖于其它类型或成员。例如一个基类的可访问级别最低要和派生类一致:
class BaseClass {…}
public class MyClass: BaseClass {…} // Error
上面的代码因为BaseClass的可访问级别internal低于MyClass的public编译器会报错。

参考:https://msdn.microsoft.com/zh-cn/library/wxh6fsc7.aspx

2、C++没有静态类,即不会有static class Base {…}这种定义,而C#有。
C#静态类中的成员变量和函数必须都是static的。它不能被继承,不能实例化,不能有实例化的构造函数,但可以有静态构造函数。

参考:http://blog.csdn.net/xiaobai1593/article/details/7278014
https://msdn.microsoft.com/zh-cn/library/98f28cdx.aspx

Unity LOD

LOD全称Level of Detail,是Unity提供的一项提高渲染性能的特性。它根据摄像机和对象的距离来渲染对象不同精度的mesh,越远时所需渲染的mesh精度会越低。

Unity中GameObject通过添加LOD Group组件来使用LOD特性。参考:http://docs.unity3d.com/Manual/class-LODGroup.html

InspectorLODGroup

LOD Group中可以设置多个level,如LOD:0、LOD:1、LOD:2等。不同level会设置一个百分比,当对象包围盒的高度相对屏幕高度的百分比小于这个百分比时就会进入这个level。

需要注意的是当QualitySettings中LOD Bias的设置不为1时,发生两个level间的过渡,摄像机所在的位置不会是两个level间之前设置好的位置(百分比),而是会有个由LOD Bias决定的偏差。LOD Bias的设置越趋近于0,对象越趋近于Culled。

不同level还可以设置它们的renderers,renderer实际上是一个拥有相应level的mesh的GameObject,通常它就是使用LOD Group组件的对象的孩子节点。

LOD的详细介绍参考:http://docs.unity3d.com/Manual/LevelOfDetail.html

Unity编辑器环境和项目出的手机版本可能会出现LOD表现不一致的情况,通常是由于LOD Bias引起的。Unity的默认QualitySettings在编辑器下会选择Fantastic而出版本会选择Fasttest。

1

如图Fantastic项上有白色蒙板表示编辑器下选择的品质,而绿色的checkbox表示不同平台出版本时的品质。不同品质下的Lod Bias默认设置会不一样。

2

Unity各品质的Lod Bias默认值分别为:Fastest 0.3、Fast 0.4、Simple 0.7、Good 1、Beautiful 1.5、Fantastic 2。在编辑器中可以通过LOD Group组件查看当前的LOD Bias的值,如下图。

3

当出现不同平台LOD表现不一致时记得检查QualitySettings。参考:http://docs.unity3d.com/Manual/class-QualitySettings.html

UICamera

UICamera是NGUI里很重要的一个脚本,它负责UI事件的通知,是NGUI里UI事件的生产者和投递者。它需要attach到一个绘制NGUI里UI控件的camera上,但它和UI控件的绘制没有任何关系——没有被UICamera attached的camera依然可以正常绘制UI,只是这些UI控件不会收到UI事件的通知。

UICamera支持的UI事件如下。

1

UICamera本质上是对从Unity Input中获取的输入数据进行了封装和分发。它根据输入设备的不同将输入模式分为了三类,即Mouse(鼠标)、Touch(触摸屏)和Controller(手柄等)。

2

不同模式下对UI事件的生成会有不同的处理,后面会详细讲到。

UICamera中将事件类型分为World和UI两类,它和eventReceiverMask、rangeDistance一起影响着如何通过raycasts找到输入位置的GameObject。

3

4

5

当EventType为World时UICamera采用GameObject的实际距离判定射线碰撞到的最近的对象,为UI时则采用UIWidget的raycastDepth来判断。eventReceiverMask指定了哪些层能发生和接收UI事件,rangeDistance则指定了能发生和接收UI事件的对象的距离范围。这三者一起决定了UICamera.Raycast方法的执行逻辑和结果。

6

UICamera中还有一些细节的配置项(成员变量),这里就不一一详述了,下面介绍下UICamera里的核心数据类型——MouseOrTouch。

7

MouseOrTouch是一个UI事件的抽象,用于保存输入数据中的位置、偏移量等重要信息。

pos:当前帧输入数据的位置信息。

lastPos:前一次输入数据的位置信息,不过好像没用到。

delta:当前帧与上一帧输入数据的位置偏移。

totalDelta:当前帧与事件最开始发生时输入数据的位置偏移,用于drag事件。

pressedCam:发生press事件时的camera。

last:前一次输入位置的对象。

current:当前帧输入位置的对象。

pressed:press的对象。

dragged:drag的对象。

clickTime:click事件分发出去的时间,用于判定double click事件的分发。

clickNotification:OnClick事件的发生条件,None为不发生,Always为总是发生,BasedOnDelta为根据位置移动的偏移量来决定是否发生。

touchBegan:用于Touch模式下标识一个touch是否为began阶段。

pressStarted:标识press事件是否开始。

dragStarted:表示drag事件是否开始。

有了MouseOrTouch,UICamera就通过它来保存不同输入设备对应的输入数据。

8

mMouse、mTouches、controller就分别对应了鼠标、触摸屏和手柄等的输入数据。鼠标分左键、右键和滚轮,因此由一个包含3个MouseOrTouch对象的数组组成。而触摸屏会有多点触控发生,因此是一个以fingerId为key的Dictionary。

UICamera的事件生成和分发会在每一帧Update时做检测,其主要逻辑如下:

1、处理触摸屏或鼠标事件;

2、通过OnCustomInput的delegate允许使用者自定义的输入处理;

3、处理选中状态的控件;

4、处理键盘和手柄事件;

这里选取触摸屏事件的处理做简单分析:

public void ProcessTouches ()

{

currentScheme = ControlScheme.Touch; // 设置输入模式为Touch

for (int i = 0; i < Input.touchCount; ++i) // 遍历输入数据中的每一个Touch

{

Touch touch = Input.GetTouch(i);

// 获取并设置当前的MouseOrTouch

currentTouchID = allowMultiTouch ? touch.fingerId : 1;

currentTouch = GetTouch(currentTouchID);

// 判断当前的touch状态

bool pressed = (touch.phase == TouchPhase.Began) || currentTouch.touchBegan;

bool unpressed = (touch.phase == TouchPhase.Canceled) || (touch.phase == TouchPhase.Ended);

currentTouch.touchBegan = false;

// 设置当前touch的位置信息

currentTouch.delta = pressed ? Vector2.zero : touch.position – currentTouch.pos;

currentTouch.pos = touch.position;

// 通过Raycast获取hoveredObject并更新touch相关信息

if (!Raycast(currentTouch.pos, out lastHit)) hoveredObject = fallThrough;

if (hoveredObject == null) hoveredObject = genericEventHandler;

currentTouch.last = currentTouch.current;

currentTouch.current = hoveredObject;

lastTouchPosition = currentTouch.pos;

if (pressed) currentTouch.pressedCam = currentCamera;

else if (currentTouch.pressed != null) currentCamera = currentTouch.pressedCam;

// 设置touch的clickTime,用于双击事件的判定

if (touch.tapCount > 1) currentTouch.clickTime = RealTime.time;

// 事件的分发

ProcessTouch(pressed, unpressed);

// 当前touch的事件分发完成后进行重置

if (unpressed) RemoveTouch(currentTouchID);

currentTouch.last = null;

currentTouch = null;

// 不支持多点触控则只处理第一个touch

if (!allowMultiTouch) break;

}

// 没有触控的输入数据时则考虑鼠标等输入设备的输入

if (Input.touchCount == 0)

{

if (useMouse) ProcessMouse();

#if UNITY_EDITOR

else ProcessFakeTouches();

#endif

}

}

从ProcessTouches的处理可以看出真正的事件分发在ProcessTouch里,不仅是这里的触控事件分发,其它如鼠标的事件分发等最终都会调用到它。这里就不再列出全部源码,仅截取一个小片段来分析。

9

ProcessTouch里当前事件处于pressed状态时,先通过第一个Nofity通知原来处于pressed状态的对象响应OnPress(false)消息,再通过第二个Notify通知当前处于pressed状态的对象响应OnPress(true)消息。而Notify又是如何做到通知对象响应消息的呢?答案是Unity的SendMessage。

10

这样UICamera就完成了从Unity Input中获取输入数据,再封装成事件,最终又通过Unity SendMessage分发消息的整个闭环流程。

Beginning iOS 7 Development Exploring the iOS SDK 1-4

iOS编程的一些区别:
1、任何时候只有一个应用处于active状态并显示在屏幕上;
2、每个应用都只有一个window;
3、每个应用都只能访问自己的sandbox目录;
4、每个应用都要在限定的时间内做出响应(用户按下home键,应用保存数据等操作的时长超过5s就会被系统kill掉);
5、有限大小的屏幕;
6、有限的系统资源。

Xcode navigator从左到右依次为:
Project navigator
Symbol navigator
Find navigator
Issue navigator
Test navigator
Debug navigator
Breakpoint navigator
Log navigator
快捷键依次为command + 1/2/3/4/5/6/7/8

Xcode快捷键:
shift+command+O: Open Quickly

Interface Builder支持.nib、.xib和.storyboard文件。
.nib:最早的二进制格式
.xib:XML格式,.nib和.xib通称nib file
.storyboard:meta-nib file
nib file和storyboard还有一个区别:nib file通常一个文件只包含一个single view及其里面的控件,当这个view需要显示时只需加载这个nib节约内存;而storyboard可以包含所有界面但只加载其中特定的部分。

按住Option键移动鼠标到类名上可以查看类的描述
选中Image View使用快捷键Command+=或者使用菜单Editor->Size to Fit Content可以将Image View调整为设置的图片大小
为UI添加约束:Editor->Resolve Auto Layout Issues->Add Missing Constraints